我认为,为了理解这一点,必须首先了解类型和类之间的细微区别:对象有类,引用有类型。
类与类型
我认为一个例子是解释我在这里的意思的好方法。假设存在以下类层次结构:
class Mammal {}
class Feline extends Mammal {}
class Lion extends Feline {}
class Tiger extends Feline {}
由于子类型多态性,我们可以声明对同一对象的多个引用。
Tiger tiger = new Tiger(); //!
Feline feline = tiger;
Mammal mammal = feline;
有趣的是,如果我们问这些参考文献中的每个人他们的班级名称,他们都会用相同的答案回答:“老虎”。
System.out.println(tiger.getClass().getName()); //yields Tiger
System.out.println(feline.getClass().getName()); //yields Tiger
System.out.println(mammal.getClass().getName()); //yield Tiger
这意味着对象的实际类是固定的,它的类是我们在上面的代码中使用“new”运算符时用来实例化它的类。
另一方面,引用可能具有不同的类型,与实际的对象类(即在这种情况下的老虎)多态兼容。
因此,对象具有固定的类,而引用具有兼容的类型。
这往往会令人困惑,因为类名与我们用来命名引用类型的东西相同,但从语义上讲,正如我们在上面看到的那样,存在细微的差异。
也许最令人困惑的部分是认识到类也是对象,因此它们可以有自己的兼容引用。例如:
Class<Tiger> tigerClass = null;
Class<? extends Tiger> someTiger = tigerClass;
Class<? extends Feline> someFeline = tigerClass;
Class<? extends Mammal> someMammal = tigerClass;
在我上面的代码中,被引用的对象是一个类对象(我暂时将其保留为空),并且此处使用的这些引用具有不同的类型来访问该假设对象。
所以,你看,这里的“class”这个词是用来命名一个“引用类型”的,指向一个实际的类对象,其类型与任何这些引用兼容。
在上面的示例中,由于我将原始变量初始化为null,因此我无法定义此类类对象。这是有意为之,一会儿我们就会明白为什么。
关于在引用上调用 getClass
和 getSubclass
下面@Jatin考虑该方法的示例,现在,让我们考虑以下多态代码:getClass
Mammal animal = new Tiger();
现在我们知道,无论我们的引用是类型,对象的实际类是,并且永远是(即类)。animal
Mammal
Tiger
如果我们这样做,我们应该得到什么?
? type = mammal.getClass()
应该是 a 还是 a ?type
Class<Mammal>
Class<Tiger>
好吧,问题是,当编译器看到类型为的引用时,它无法分辨出该引用指向的对象的实际类是什么。这只能在运行时确定,对吧?它实际上可能是哺乳动物,但它也可能是它的任何子类,比如老虎,对吧?Mammal
因此,当我们要求它的类时,我们没有得到,因为编译器无法确定。相反,我们得到一个,这更有意义,因为毕竟,编译器知道,根据子类型多态性的规则,给定的引用可能指向哺乳动物或其任何子类型。Class<Mammal>
Class<? extends Mammal>
在这一点上,你可能会看到在这里使用单词类的微妙细微差别。看起来,我们实际上从该方法中得到的是某种类型引用,我们用它来指向原始对象的实际类,正如我们之前已经解释过的那样。getClass()
好吧,关于方法也可以说同样的事情。例如:asSubclass
Mammal tiger = new Tiger();
Class<? extends Mammal> tigerAsMammal = tiger.getClass();
Class<? extends Feline> tigerAsFeline = tigerAsMammal.asSubclass(Feline.class);
当我们调用时,我们得到的是对引用所指向的实际类类型的引用,但是编译器无法再确定该实际性质应该是什么,因此您会得到一个更宽松的引用,例如.这是编译器可以假设的关于对象原始性质的最多,这就是为什么我们得到的就是这个。asSubclass
Class<? extends Feline>
那么新的Tiger().gerClass()呢?
我们可能期望获得(没有wirldcards)的唯一方法应该是访问原始对象,对吧?喜欢:Class<Tiger>
Class<Tiger> tigerClass = new Tiger().getClass()
不过,有趣的是,我们总是通过引用类型到达老虎对象,对吧?在Java中,我们永远无法直接访问对象。由于我们总是通过其引用到达对象,因此编译器无法对返回的引用的实际类型做出假设。
这就是为什么即使这个代码也会产生Class<? extend Tiger>
Class<? extends Tiger> tigerClass = new Tiger().getClass();
也就是说,编译器不保证运算符可能在此处返回的内容。对于所有重要的事情,它可能会返回与Tiger兼容的对象,但不一定是其类是Tiger本身的对象。new
如果更改工厂方法的运算符,这将变得更加清晰。new
TigerFactory factory = new TigerFactory();
Class<? extends Tiger> tigerClass = tigerFactory.newTiger().getClass();
还有我们的老虎工厂:
class TigerFactory {
public Tiger newTiger(){
return new Tiger(){ } //notice this is an anonymous class
}
}
我希望这能以某种方式促进讨论。