kotlin和java中遇到的问题(以前没有关注过的)

先看一段代码,猜猜结果是什么。

public class People2 {
    String name;

    public People2(String name) {
        this.name = name;
    }

    void test1() {
        System.out.println(this.getClass()); // 这里是class Student2
        System.out.println(this.name);
    }

    public static void main(String[] args) {
        // 是输出zyt还是People2
        new Student2("zyt").test1();
        
    }

}

class Student2 extends People2 {

    String name;

    public Student2(String name) {
        super("People2");
        this.name = name;
    }
}

答案肯定是People2.

以前没有注意到这个问题哦,我是在kotlin语法中注意到这个问题,然后用JAVA试了一下也一样。先看一下这段代码,然后在看kotlin代码。

自己的理解:

首先,我们引用方式是Student2.test1()。按照我们一贯的想法应该是class Student2 和 zyt。但是结果却是class Student2People2。我们需要注意一点就是在Student2这个构造器调用父类构造器时和我们以往的是不同的,我们以往就是直接super(name)类似于这种。有种肌肉记忆的感觉,也没有多想。但是,昨天在看kotlin时,kotlin语法中是有属性覆盖(override)这个概念的,我在JAVA中并没有注意到这个概念(其实我是真没听说过。。。)。
那么在这里的代码中,它是如何运行的呢?

  1. 首先,调用的方法test1()是父类中的方法,那么方法体中的this应该指的是父类型People2,但是由于继承,子类实例也可以调用父类中方法。在我们的这个例子中,我们希望打印相应调用实例的name属性。可是回过头来想,我们在编写test1()中函数体,当我们输入this.的时候编译器的提示肯定只是当前类的属性,那么在运行的时候也应该是从当前类(People2)中进行查找,可是在这里我们想通过调用的实例类型来打印相应实例的属性值信息,我们应该怎么办?把子类构造器中的super("People2")改为super(name),其实在这里我们就可以理解为子类的属性覆盖了父类中的属性,

  2. 我觉得kotlin代码更好解释,如下

    fun main(){
        val people = People()
        val worker = Worker("zyt")
        people.funOfPeople(worker)
    
    }
    
    open class People(
        private val name: String = "People" // 当声明为private时,就表明该属性不可被覆盖
    ) {
    
        fun funOfPeople(people: People) {
            println(people.javaClass)
            println(people.name)
        }
    
        fun fun2OfPeople() {
            println(this.name)
        }
    }
    
    class Worker(
        val name: String
    ) : People()
    

    在上面的kotlin代码中,最后输出的是class Worker 和 People。我们的迷惑在println(people.name)这一语句,在编译时,当我们输入people.时,编辑器的提示属性肯定是该类中的属性,不可能出现子类中的属性,有因为该属性由private关键字修饰,表示子类不可覆盖该属性,所以在运行时打印的也应该是People中的属性值。迷惑点在于子类和父类的属性名字一样。

    那么如何实现我们想要的结果呢(根据传入的类型打印相应类型的属性值),那么就需要将属性进行覆盖,代码如下。

    open class People(
        // 这里就表明,有子类覆盖该属性,那么下面方法中传入的参数类型为子类型,调用的肯定是子类实例的属性
        open val name: String = "People" 
    ) {
    
        fun funOfPeople(people: People) {
            println(people.javaClass)
            println(people.name)
        }
    
        fun fun2OfPeople() {
            println(this.name)
        }
    }
    
    class Worker(
        override val name: String
    ) : People()
    

    再用JAVA进行对比

    public class People2 {
    
        String name; // 没有添加final 和kotlin进行对比
    
        public People2(String name) {
            System.out.println("People2 构造器传入的name参数:" + name);
            this.name = "People";
        }
    
        void test1() {
            System.out.println(this.getClass());
            // 如果说通过引用地址来查找,那么这个this指代的实例中是有name属性,
            // 那么如果先从上往下找,先从父类中查找,在往下(子类)中查找,有点说不过去,因为父类中肯定有这个属性,
            // 要不然编译器不通过,既然父类中有了,我觉得就没有必要再往下找了
            // TODO: 2021/8/22 java并中没有属性覆盖的概念,只有方法覆盖的概念
            // 如果说属性可以覆盖,那么下面打印的应该是zyt,而不是People,所以调用的还是父类中的属性
            System.out.println(this.name); // 子类和父类的属性名字一样的话,调用的也是父类中的属性,那么下面的方法为什么没有问题呢
            this.overrideFun();// 因为JAVA中有方法覆盖的概念,子类实例调用的还是子类中的方法(属性不适用)
        }
    
        public static void main(String[] args) {
            new Student2("zyt").test1();
        }
    
        void overrideFun() {
            System.out.println("overrideFun in People2");
        }
    
    }
    
    class Student2 extends People2 {
    
        String name;
    
        public Student2(String name) {
            super(name);
            this.name = name;
        }
    
        void overrideFun() {
            System.out.println("overrideFun in Student2");
        }
    }
    

    我的总结(我个人的理解):

    ​ JAVA中并没有属性覆盖的概念,只有方法覆盖的概念。

posted @ 2021-08-22 10:32  SowHappy  阅读(81)  评论(0)    收藏  举报