我有两个班级:
public class Base{
public Derived becomeDerived(){
Derived d = new Derived(this);
//set this pointer pointing to d,
//this = d; doesn't work
return d;
}
}
public class Derived extends Base{
public Derived(Base b){ }
}
是否可以通过我在示例中显示的方法更改当前对象的运行时类型?
我想要这样做的原因是提供了一种连接对象的方法。
我有
public abstract class Table{
}
和
public class ComplexTable extends Table{ }
实际上是Table
个对象的链接列表。
我想提供一种方法,比如Table.append(Table t)
,它不仅可以修改当前的Table对象,还可以使它成为ComplexTable
的实例。
答案 0 :(得分:3)
没有
在您的示例中,Base
不会成为Derived
,它会返回一个新的Derived
对象。
Base foo = new Base();
foo = foo.becomeDerived();
这可能是什么让你失望,记住变量不是对象,只是对一个的引用。因此,虽然您可以说foo
从Base
更改为Derived
,但对象的运行时类型没有更改,您创建了具有不同运行时类型的新对象并重新分配变量。
编辑: 更深入的示例。我给对象“名字”只是为了让它更容易理解。
Base foo = new Base();
/* After execution:
*
* Vars: | Objects:
* foo ----+---> a Base object (Name: Joe)
*/
foo = foo.becomeDerived();
/* After execution:
*
* Vars: | Objects:
* foo ----+---> a Derived object (Name: Phil)
* | a Base object (Name: Joe)(notice nothing references it)
*/
“乔”的类型没有改变。乔曾经并将永远是Base
对象。这就是你所说的“对象的运行时类型”。但是,变量的运行时类型会一直更改!在此示例中,foo
以Base
开头,但变为Derived
}。
答案 1 :(得分:1)
您无法将this
设置为d
,因为this
是Derived d
的超级类型。
但是,在这种情况下,可以将Derived
类型的对象(例如d
)存储到类型Base
的引用中。
您可以在引用基类时存储派生类的类型。但它并不是在技术上改变类型只是派生类型的引用保持对象。
答案 2 :(得分:1)
与C ++不同
您无法更改或重新分配
的值this
。
This
被选为保留字。所以答案是
否,无法更改当前对象的运行时类型
我在代码中发现的另一个错误总是使用Base引用变量,因此您可以引用扩展它的类的对象。
Base b;
b=new Derived();
b=new Base();
答案 3 :(得分:0)
错误的想法紧随其后
可以更改对象的类型,但是它依赖的行为不属于JVM规范,因此不可移植。您已被警告。
有一个类sun.misc.Unsafe
,让您可以在对象的任何偏移处读取/写入内存。在64位JVM上,对象的类型存储在object's header中,偏移量为8。因此,要更改对象的类型,您要做的就是更改此偏移量的值。请注意,在它们之间进行切换的类型/类必须具有相同的结构(相同偏移量且总大小相同的参考字段)。否则,垃圾收集器将读取非引用作为引用(反之亦然),并使JVM崩溃。
我不故意提供工作示例,因为我不推荐这样做。这样的东西在C中比在Java中更合适。其实并不难,而且我提供的链接包含了所有必需的信息。
我进行的一些测试表明,它可以在多个JVM上运行,并且JIT可以抵抗这些危险的对象类型更改。这不能保证它可以在所有系统和所有条件下正常工作。
顺便说一句,我很想听到有人可以解释为什么JIT不将对象的类型视为jit编译时常量,或者JIT如何知道当类型为JIT时要重新编译的原因。对象已更改。