Java Covariants

Don*_*onX 14 java

public class CovariantTest {
    public A getObj() {
        return new A();
    }

    public static void main(String[] args) {
        CovariantTest c = new SubCovariantTest();
        System.out.println(c.getObj().x);
    }
}

class SubCovariantTest extends CovariantTest {
    public B getObj() {
        return new B();
    }
}

class A {
    int x = 5;
}

class B extends A {
    int x = 6;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码在编译和运行时打印5.它使用过度返回方法的协变返回.

为什么它会打印5而不是6,因为它在SubCovariantTest类中执行overRidden方法getObj.

有人可以对此有所了解.谢谢.

Law*_*Dol 15

这是因为在Java成员变量不覆盖时,它们会影响(与方法不同).A和B都有变量x.由于c被声明为CovarientTest类型,因此getObj()的返回隐式为A,而不是B,因此得到A的x,而不是B的x.


Spo*_*ike 11

Java 不会覆盖字段(aka.属性或成员变量).相反,他们互相遮蔽.如果您通过调试器运行程序,您将x在任何类型的对象中找到两个变量B.

这是对正在发生的事情的解释.程序首先检索隐式类型的东西,A然后调用x假定来自的东西A.尽管它显然是一个子类型,但在您的示例中,类型的对象B是通过创建的SubCovariantTest,它仍然假设您在getObj()中返回隐式类型为A的内容.由于Java无法覆盖字段,因此测试将调用A.x而不是B.x.

CovariantTest c = new SubCovariantTest();
// c is assumed the type of CovariantTest as it is
// implicitly declared

System.out.println(c.getObj().x);
// In this method chain the following happens:

// c.getObj() will return object of type B
// BUT will assume it is an A

// c.getObj().x will return the x from A
// since in this context the compiler assumes 
// it is an A and make the call to A.x
Run Code Online (Sandbox Code Playgroud)

它似乎是一个令人难以置信的陷阱,因为方法总是在Java中被覆盖(与C++和C#相比,它们不是).您通常不会遇到此问题,因为Java代码约定会告诉您永远不要直接访问字段.而是确保始终通过访问器方法(即getter)访问字段:

class A {
    private int x = 5;

    public int getX() { // <-- This is a typical accessor method
        return x;
    }
}

class B extends A {
    private int x = 6;

    @override
    public int getX() {
        // will be called instead even though B is implied to be A
        // @override is optional because methods in Java are always virtual
        // thus are always overridden
        return x;
    }
}
Run Code Online (Sandbox Code Playgroud)

使此工作的代码如下:

c.getObj().getX();
// Will now call getX() in B and return the x that is defined in B's context.
Run Code Online (Sandbox Code Playgroud)


小智 10

将上面的A和B替换为:

class A {
    public int getX() { return 5; }
}

class B extends A {
    public int getX() { return 6; }
 }
Run Code Online (Sandbox Code Playgroud)

这可能会回答你关于错误的问题;-)


sta*_*lue 9

在对象中有两个名为x的字段,一个来自A类,一个来自B类,它们隐藏了A中的字段.由于c的声明,字段x引用的是A中的字段.

在实践中,这不是一个问题,因为它是非常糟糕的风格

  • 隐藏子类中的字段,

  • 直接访问字段而不是通过方法.