简体   繁体   English

将接口转换为 class 是如何工作的?

[英]How does this casting of an interface to a class work?

IY iy = new D();
C c1 = (C) iy; // What is going on here...
c1.doOther();  // and here?

public interface IX {
    void doIt();
}

public interface IY {
    void doOther();
}

public class A {
    public void doIt(double d) {
        System.out.println("Doit A " + d);
    }
}

public class B extends A implements IX {
    public void doIt() {
        System.out.println("Doit B");
    }

    public void doIt(int i) {
        System.out.println("Doit B " + i);
    }
}

public class C extends A implements IY {
    public void doIt() {
        System.out.println("Doit C");
    }

    public void doOther() {
        System.out.println("DoOther " + this.getClass().getSimpleName());
    }
}

public class D extends C {
    public void doIt() {
        System.out.println("Doit D");
    }
}

Prints out DoOther D打印出DoOther D


Why does this happen?为什么会这样? I am learning about Javas type system and I am super confused.我正在学习 Java 类型系统,我非常困惑。

What exactly is happening on each step?每一步到底发生了什么? I will try and give my understanding of what is happening.我会尽力说明我对正在发生的事情的理解。

IY iy = new D() compiles and runs fine due to the fact that D inherits from C which inherits from IY . IY iy = new D()编译并运行良好,因为D继承自C继承自IY Due to the transitivity rule (D <: C <: IY => D <: IY) D is sub to IY and it works to store a D-object into a variable of type IY because D is a type of IY .由于传递性规则(D <: C <: IY => D <: IY) DIY的子类,它可以将D-object存储到IY类型的变量中,因为DIY的类型。

C c1 = (C) iy , here is where I get confused. C c1 = (C) iy ,这就是我感到困惑的地方。 To my understanding we are casting a D-object into a C-object .据我了解,我们正在将D-object转换为C-object Or is it something else that happens?还是发生了其他事情? Am I maybe casting a variable of type IY (interface) to the type C ?我是否可以将IY类型(接口)的变量转换为C类型? The type changes, but the value of the variable (which is a reference to a D-object ) remains the same between iy and c1 ?类型发生了变化,但变量的值(它是对D-object的引用)在iyc1之间保持不变?

This works because C is super to D .这是有效的,因为CD来说是超级的。 Now this is a dangerous thing to do if I have understood this correctly, due to the fact that this will not always run because it is not guaranteed that C and D have a relationship, therefore it can fail during runtime and give the ClassCastException exception, is this correct?现在,如果我正确理解了这一点,这是一件危险的事情,因为它不会总是运行,因为不能保证CD有关系,因此它可能在运行时失败并给出ClassCastException异常,这个对吗?

But then the result of c1.doOther() confuses me.但是c1.doOther()的结果让我感到困惑。 If c1 is a C-object and we do c1.doOther() , isn't the C-object c1 calling its own instance method doOther() ?如果c1是一个C-object并且我们执行c1.doOther() ,那么C-object c1不是调用它自己的实例方法doOther()吗? If that is the case then the line this.getClass().getSimpleName() should return DoOther C but it returns DoOther D instead as if it was the D-object calling its super method, which seems to be the actual case?如果是这种情况,那么 this.getClass this.getClass().getSimpleName()行应该返回DoOther C但它返回DoOther D就好像它是调用其超级方法的D-object ,这似乎是实际情况?

What is really going on here?这里到底发生了什么?

casting a D-object into a C-object将 D 对象转换为 C 对象

This is not how I would describe it.这不是我要描述的方式。 The cast (C) checks the type of the object.演员(C)检查object 的类型。 If it passes, then the assignment is made.如果通过,则进行分配。 If not then an error is thrown.如果不是,则抛出错误。

No changes or other from-one-"into"-another is done.没有更改或其他从一个“到”另一个完成。 The cast only tests that the object is of the requested type.强制转换仅测试 object 是否属于请求的类型。 Once that object is found to be of the requested type, then operations (methods) relating to that type are allowed.一旦发现 object 属于请求的类型,则允许与该类型相关的操作(方法)。

A corollary operation is the instanceOf keyword, which allows you the programmer to test a type without throwing an error.一个推论操作是instanceOf关键字,它允许程序员在不抛出错误的情况下测试类型。

if( iy instanceOf C ) {
   // do C stuff
}
else
   System.err.println( "I couldn't do it." );

If c1 is a C-object and we do c1.doOther(), isn't the C-object c1 calling its own instance method doOther()?如果 c1 是一个 C 对象并且我们执行 c1.doOther(),那么 C 对象 c1 不是调用它自己的实例方法 doOther() 吗? If that is the case then the line this.getClass().getSimpleName() should return DoOther C如果是这种情况,那么 this.getClass().getSimpleName() 应该返回 DoOther C

The type system is basically in two halves.类型系统基本上分为两半。 There's the type you declare to the compiler (here IY and then you cast to C ), and then there's the actual real type of the object.有你向编译器声明的类型(这里是IY ,然后你转换为C ),然后是 object 的实际类型。 Operations that are allowed (ie method calls) are based on the declared type.允许的操作(即方法调用)基于声明的类型。

However, the actual call is based on the type of the object itself, which in this case is always going to be a D regardless of the declared type.但是,实际调用是基于 object 本身的类型,在这种情况下,无论声明的类型如何,它始终是D So you always get D 's interpretation of being an IY or a C type.所以你总是得到D对作为IYC类型的解释。 It's always D that is being called here.这里总是调用D Again, there is no changing based on casting or assignment.同样,没有基于强制转换或分配的更改。 The type of D is "polymorphic" and can "look like" an IY or C , but it's always really a D . D的类型是“多态的”并且可以“看起来像” IYC ,但它始终是真正的D

TL;DR: Casting doesn't change the underlying instance's type, it just exposes it as a variable of a different type to all later code. TL;DR:强制转换不会改变底层实例的类型,它只是将它作为不同类型的变量公开给所有以后的代码。


Analogy:比喻:

You have a box, and you put an accountant named Alice in it.你有一个盒子,你在里面放了一个名叫 Alice 的会计师。 You put a label on that box that says 'Accountant'.你把 label 放在那个写着“会计师”的盒子上。

You then put another label on it that says 'senior accountant'.然后,您将另一个 label 放在上面,上面写着“高级会计师”。 This hasn't changed Alice in the box.这并没有改变盒子里的爱丽丝。 Alice doesn't do accounting any different to any other senior accountant, so they don't override that behaviour. Alice 的会计工作与任何其他高级会计师没有任何不同,因此他们不会忽视这种行为。

When you ask Alice to doAccounting , since they haven't overridden that behaviour, the basic senior accountant workflow for doAccounting is run, but as part of that workflow there is a step that gets the accountants name to sign the document.当您要求 Alice 进行doAccounting时,由于他们没有覆盖该行为,因此将运行doAccounting的基本高级会计师工作流程,但作为该工作流程的一部分,有一个步骤可以获取会计师姓名以签署文档。

Would you be surprised that the resulting signature on the document said 'Alice'.您会惊讶于文件上的结果签名是“爱丽丝”吗?


More detail:更多详情:

IY iy = new D(); // (1)
C c1 = (C) iy; // (2)
c1.doOther();  // (3)

(1) You create an instance of type D , and store it as a variable iy . (1) 创建一个D类型的实例,并将其存储为变量iy You give this variable the type IY , which means that anything using that variable will only see that is an instance of IY .您将此变量指定为IY类型,这意味着使用该变量的任何东西都只会看到它是IY的一个实例。 This does not mean that it isn't a D anymore, it's just you're not exposing that to any later code.这并不意味着它不再D ,只是你没有将它暴露给任何以后的代码。 But the variable is just a 'pointer' to the newly created D instance.但是该变量只是指向新创建的D实例的“指针”。

(2) You cast the D instance you created in step (1) to type C , and store this in a new variable c1 . (2) 您在步骤 (1) 中创建的D实例转换为类型C ,并将其存储在新变量c1中。 This is evaluated at runtime, and it succeeds as the iy variable being cast is of type D (as discussed above) and by your class definitions D extends C - ie all D s are C s (but not vice-versa).这是在运行时评估的,并且它成功,因为正在转换的iy变量是D类型(如上所述)并且通过您的 class 定义D extends C - 即所有D都是C (但不是反之亦然)。 You declare the c1 variable as having type C , which is fine - but that does not change the fact that the underlying instance is still the D that you created above.您将c1变量声明为具有类型C ,这很好 - 但这不会改变底层实例仍然是您在上面创建的D的事实。 Again, c1 is now a 'pointer' to the previous D instance.同样, c1现在是指向前一个D实例的“指针”。 You've just exposed to later code that is at least of type C .您刚刚接触到至少属于C类型的后续代码。

(3) You call an instance method doOther on your c1 variable. (3) 您在c1变量上调用实例方法doOther This is invoked on the underlying instance that c1 points to.这是在c1指向的基础实例上调用的。 As discussed above, this is still the same instance of type D that you created in step (1).如上所述,这仍然是您在步骤 (1) 中创建的类型D的同一实例。 So it looks at that instances doOther method.所以它着眼于实例doOther方法。 This then looks if type D has an override of doOther to call, but since it doesn't goes to the next one in the hierarchy which is from type C .然后,这会查看类型D是否具有要调用的doOther的覆盖,但由于它不会转到层次结构中来自类型C的下一个。 As part of the method, getClass() is used.作为该方法的一部分,使用了getClass() Much like the doOther call, this is invoked on your D instance, and that does have its own getClass() method (because all new classes will).很像doOther调用,这是在您的D实例上调用的,并且确实有自己的getClass()方法(因为所有新类都会)。 So the getClass() from type D is called, and you see the result you see.因此调用类型DgetClass() ,您会看到所看到的结果。

IY iy = new D();
C c1 = (C) iy;
c1.doOther();

As per the code you shared IY, C, A are parent of D. Whatever type you changing your D object, it is still D object only the reference are going to change, not the D object. As per the code you shared IY, C, A are parent of D. Whatever type you changing your D object, it is still D object only the reference are going to change, not the D object. When you call c1.doOther() it is calling with the D object, you are getting DoOther D.当您调用 c1.doOther() 时,它正在使用 D object 调用,您将获得 DoOther D。

The type only restrict what ever method you can call.该类型仅限制您可以调用的任何方法。 If the reference is IY then you can only able to call doOther.如果引用是 IY,那么您只能调用 doOther。 If it point to C, then you can access everything C have.如果它指向 C,那么您可以访问 C 拥有的所有内容。 It will go like this...它将像这样 go...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM