将接口转换为 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");
}
}
打印出来DoOther D
为什么会这样?我正在学习 Javas 类型系统,我超级困惑。
每一步到底发生了什么?我会尽力了解正在发生的事情。
IY iy = new D()
编译并且 运行 很好,因为 D
继承自 C
,而 C
继承自 IY
。由于传递性规则 (D <: C <: IY => D <: IY)
D
是 IY
的子集,它可以将 D-object
存储到 IY
类型的变量中,因为 D
是 IY
.
的一种
C c1 = (C) iy
,这是我感到困惑的地方。 据我了解,我们正在将 D-object
转换为 C-object
。 还是发生了其他事情?我是否可以将类型 IY
(接口)的变量转换为类型 C
?类型发生变化,但变量的值(它是对 D-object
的引用)在 iy
和 c1
?
之间保持不变
之所以有效,是因为 C
优于 D
。现在,如果我理解正确的话,这是一件危险的事情,因为这并不总是 运行 因为不能保证 C
和 D
有关系,因此它可能会在 运行 时间内失败并给出 ClassCastException
异常,这是正确的吗?
但是 c1.doOther()
的结果让我很困惑。如果 c1
是一个 C-object
而我们做 c1.doOther()
,那么 C-object c1
不是调用它自己的实例方法 doOther()
吗?如果是这种情况,那么行 this.getClass().getSimpleName()
应该 return DoOther C
但它 returns DoOther D
而不是 D-object
调用它超级方法,这似乎是实际情况?
这里究竟发生了什么?
casting a D-object into a C-object
这不是我要描述的方式。强制转换 (C)
检查 对象的类型。如果通过,则进行分配。如果不是,则抛出错误。
没有变化或其他从一个-“到”-另一个完成。强制转换仅测试对象是否属于请求的类型。一旦发现该对象属于请求的类型,则允许与该类型相关的操作(方法)。
一个推论操作是 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()? If that is the case then the line this.getClass().getSimpleName() should return DoOther C
类型系统基本上分为两半。有你向编译器声明的类型(这里是 IY
然后你转换为 C
),然后是对象的实际类型。允许的操作(即方法调用)基于声明的类型。
然而,实际的调用是基于对象本身的类型,在这种情况下,无论声明的类型如何,它总是 D
。所以你总是得到 D
对 IY
或 C
类型的解释。这里总是调用 D
。同样,没有基于演员表或分配的变化。 D
的类型是“多态的”,可以“看起来像”IY
或 C
,但它实际上始终是 D
.
长话短说:
转换不会更改基础 实例的 类型,它只是将其作为不同类型的变量公开给所有以后的代码。
类比:
你有一个盒子,里面放了一个叫爱丽丝的会计师。你在那个盒子上贴了一个标签,上面写着 'Accountant'.
然后您在上面贴上另一个标签,上面写着 'senior accountant'。这并没有改变盒子里的爱丽丝。 Alice 的会计工作与其他任何高级会计师没有任何不同,因此他们不会忽视这种行为。
当您要求爱丽丝 doAccounting
时,由于他们没有覆盖该行为,doAccounting
的基本高级会计师工作流程是 运行,但 该工作流程的一部分 有一个步骤让会计师姓名签署文件。
文档上的最终签名写着 'Alice',您会感到惊讶吗?
更多详情:
IY iy = new D(); // (1)
C c1 = (C) iy; // (2)
c1.doOther(); // (3)
(1) 您创建类型 D
的实例,并将其存储为变量 iy
。你给这个变量类型 IY
,这意味着任何使用该变量的东西都只会看到它是 IY
的一个实例。这并不意味着它 不再是 一个 D
,只是您没有将其暴露给任何以后的代码。但是这个变量只是一个 'pointer' 到新创建的 D
实例。
(2) 您 cast 您在步骤 (1) 中创建的 D
实例以键入 C
,并将其存储在新变量 c1
。这是在 运行 时评估的,并且它成功,因为正在转换的 iy
变量是 D
类型(如上所述)并且由您的 class 定义 D extends C
- 即所有 D
都是 C
(但反之则不然)。您将 c1
变量声明为 C
类型,这很好 - 但这不会改变基础实例仍然是您在上面创建的 D
这一事实。同样,c1
现在是前一个 D
实例的 'pointer'。您刚刚接触到 至少 类型 C
.
的后续代码
(3) 您在 c1
变量上调用实例方法 doOther
。这是在 c1
指向的基础 实例 上调用的。如上所述,这仍然是您在步骤 (1) 中创建的类型 D
的同一实例。所以它会查看 instances doOther
方法。然后这会查看类型 D
是否具有要调用的 doOther
的覆盖,但是因为它不会转到层次结构中来自类型 C
的下一个。作为该方法的一部分,使用 getClass()
。与 doOther
调用非常相似,它是在您的 D
实例上调用的,并且 确实 有自己的 getClass()
方法(因为所有新的 classes 会)。所以 D
类型的 getClass()
被调用,你会看到你看到的结果。
IY iy = new D();
C c1 = (C) iy;
c1.doOther();
根据您共享的代码 IY,C、A 是 D 的父级。无论您更改 D 对象的类型是什么,它仍然是 D 对象,只是引用会发生变化,不会更改 D 对象。当您调用 c1.doOther() 时,它正在调用 D 对象,您将获得 DoOther D.
类型只限制你可以调用的方法。如果引用是 IY 那么你只能调用 doOther。如果它指向 C,那么您可以访问 C 拥有的所有内容。它会像这样...
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");
}
}
打印出来DoOther D
为什么会这样?我正在学习 Javas 类型系统,我超级困惑。
每一步到底发生了什么?我会尽力了解正在发生的事情。
IY iy = new D()
编译并且 运行 很好,因为 D
继承自 C
,而 C
继承自 IY
。由于传递性规则 (D <: C <: IY => D <: IY)
D
是 IY
的子集,它可以将 D-object
存储到 IY
类型的变量中,因为 D
是 IY
.
C c1 = (C) iy
,这是我感到困惑的地方。 据我了解,我们正在将 D-object
转换为 C-object
。 还是发生了其他事情?我是否可以将类型 IY
(接口)的变量转换为类型 C
?类型发生变化,但变量的值(它是对 D-object
的引用)在 iy
和 c1
?
之所以有效,是因为 C
优于 D
。现在,如果我理解正确的话,这是一件危险的事情,因为这并不总是 运行 因为不能保证 C
和 D
有关系,因此它可能会在 运行 时间内失败并给出 ClassCastException
异常,这是正确的吗?
但是 c1.doOther()
的结果让我很困惑。如果 c1
是一个 C-object
而我们做 c1.doOther()
,那么 C-object c1
不是调用它自己的实例方法 doOther()
吗?如果是这种情况,那么行 this.getClass().getSimpleName()
应该 return DoOther C
但它 returns DoOther D
而不是 D-object
调用它超级方法,这似乎是实际情况?
这里究竟发生了什么?
casting a D-object into a C-object
这不是我要描述的方式。强制转换 (C)
检查 对象的类型。如果通过,则进行分配。如果不是,则抛出错误。
没有变化或其他从一个-“到”-另一个完成。强制转换仅测试对象是否属于请求的类型。一旦发现该对象属于请求的类型,则允许与该类型相关的操作(方法)。
一个推论操作是 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()? If that is the case then the line this.getClass().getSimpleName() should return DoOther C
类型系统基本上分为两半。有你向编译器声明的类型(这里是 IY
然后你转换为 C
),然后是对象的实际类型。允许的操作(即方法调用)基于声明的类型。
然而,实际的调用是基于对象本身的类型,在这种情况下,无论声明的类型如何,它总是 D
。所以你总是得到 D
对 IY
或 C
类型的解释。这里总是调用 D
。同样,没有基于演员表或分配的变化。 D
的类型是“多态的”,可以“看起来像”IY
或 C
,但它实际上始终是 D
.
长话短说: 转换不会更改基础 实例的 类型,它只是将其作为不同类型的变量公开给所有以后的代码。
类比:
你有一个盒子,里面放了一个叫爱丽丝的会计师。你在那个盒子上贴了一个标签,上面写着 'Accountant'.
然后您在上面贴上另一个标签,上面写着 'senior accountant'。这并没有改变盒子里的爱丽丝。 Alice 的会计工作与其他任何高级会计师没有任何不同,因此他们不会忽视这种行为。
当您要求爱丽丝 doAccounting
时,由于他们没有覆盖该行为,doAccounting
的基本高级会计师工作流程是 运行,但 该工作流程的一部分 有一个步骤让会计师姓名签署文件。
文档上的最终签名写着 'Alice',您会感到惊讶吗?
更多详情:
IY iy = new D(); // (1)
C c1 = (C) iy; // (2)
c1.doOther(); // (3)
(1) 您创建类型 D
的实例,并将其存储为变量 iy
。你给这个变量类型 IY
,这意味着任何使用该变量的东西都只会看到它是 IY
的一个实例。这并不意味着它 不再是 一个 D
,只是您没有将其暴露给任何以后的代码。但是这个变量只是一个 'pointer' 到新创建的 D
实例。
(2) 您 cast 您在步骤 (1) 中创建的 D
实例以键入 C
,并将其存储在新变量 c1
。这是在 运行 时评估的,并且它成功,因为正在转换的 iy
变量是 D
类型(如上所述)并且由您的 class 定义 D extends C
- 即所有 D
都是 C
(但反之则不然)。您将 c1
变量声明为 C
类型,这很好 - 但这不会改变基础实例仍然是您在上面创建的 D
这一事实。同样,c1
现在是前一个 D
实例的 'pointer'。您刚刚接触到 至少 类型 C
.
(3) 您在 c1
变量上调用实例方法 doOther
。这是在 c1
指向的基础 实例 上调用的。如上所述,这仍然是您在步骤 (1) 中创建的类型 D
的同一实例。所以它会查看 instances doOther
方法。然后这会查看类型 D
是否具有要调用的 doOther
的覆盖,但是因为它不会转到层次结构中来自类型 C
的下一个。作为该方法的一部分,使用 getClass()
。与 doOther
调用非常相似,它是在您的 D
实例上调用的,并且 确实 有自己的 getClass()
方法(因为所有新的 classes 会)。所以 D
类型的 getClass()
被调用,你会看到你看到的结果。
IY iy = new D();
C c1 = (C) iy;
c1.doOther();
根据您共享的代码 IY,C、A 是 D 的父级。无论您更改 D 对象的类型是什么,它仍然是 D 对象,只是引用会发生变化,不会更改 D 对象。当您调用 c1.doOther() 时,它正在调用 D 对象,您将获得 DoOther D.
类型只限制你可以调用的方法。如果引用是 IY 那么你只能调用 doOther。如果它指向 C,那么您可以访问 C 拥有的所有内容。它会像这样...