是否可以通过其方法更改对象的运行时类型?

时间:2015-05-22 16:02:17

标签: java class

我有两个班级:

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的实例。

4 个答案:

答案 0 :(得分:3)

没有

在您的示例中,Base不会成为Derived,它会返回一个新的Derived对象。

Base foo = new Base();
foo = foo.becomeDerived();

这可能是什么让你失望,记住变量不是对象,只是对一个的引用。因此,虽然您可以说fooBase更改为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对象。这就是你所说的“对象的运行时类型”。但是,变量的运行时类型会一直更改在此示例中,fooBase开头,但变为Derived }。

答案 1 :(得分:1)

您无法将this设置为d,因为thisDerived 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时要重新编译的原因。对象已更改。