在编译器中隐藏对象类型

本文关键字:对象 类型 隐藏 编译器 | 更新日期: 2023-09-27 18:03:45

我在一次面试中被问到这个问题。这让我措手不及。

你有一个超类和一个子类。类BA的一个子类。这两个类都实现了相同的函数foo()

是否有可能编写一段代码,使编译器不知道对象的类型是什么?本例中对象为下面的b

A b = new B();

那么你可以调用一个函数,在执行之前不知道该函数的类型。

这是我在白板上的答案:(A)b.foo();

在编译器中隐藏对象类型

在c#中,您可以使用dynamic关键字来实现动态类型。

public void MyMethod(dynamic myVar) 
{
    // The compiler don't know the exact type of myVar
    // Does myVar have a foo() method? if not, we get a runtime error.
    myVar.foo();  
}

然而,我不认为这是他们正在寻找的答案,因为提到了AB(并且它应该适用于Java和c#)。

他们可能一直在寻找的(基本上就是你自己说的)可能是这样的:

public void MyMethod(A a)
{
    a.foo();    // What foo() method is called? The one from A or the one from B?
}

您可以使用AB的实例作为MyMethod的参数,因为BA具有is-a关系(它继承)。编译器将a视为A的实例,尽管它可能是B的实例。

调用了哪个foo()方法?这取决于B是覆盖Afoo()的实现还是隐藏它。如果它隐藏了它,A的方法被调用,这是在编译时确定的,a实际是什么类型并不重要。

如果它覆盖了foo()(这是最有可能的情况),那么它在运行时被确定。由于编译器不知道a的确切类型,因此在运行时根据a运行时类型查找要调用的正确方法。

所以编译器知道类型(如果是一个 A),但是它不知道确切的类型(它是不是一个子类?)


作为旁注。(A)b.foo();会将foo()返回的值强制转换为A。如果您想在调用foo()之前将b转换为A,您应该添加一组额外的括号:((A)b).foo();。然而,强制转换是不必要的,因为b已经被声明为A变量(A b = ..)。

你可以使用反射来创建编译器不知道的某种类型的对象(只有在运行时才会解析)。

如果你特别指的是语句new B(),那么不是——你是在告诉编译器正在创建什么类型。

至少在c#中你可以使用动态,编译器会在运行时输入代码来确定类型。