类型信息instanceof与Class的等价性
一、注册工厂
生成Pet继承结构中的对象存在着一个问题,即每次向该继承结构添加新的Pet类型时,必须将其添加为LiteralPetCreator.java中的项。如果在系统中已经存在了继承结构的常规的基础,然后在其上要添加更多的类,那么就有可能会出现问题。
你可能会考虑在每个子类中添加静态初始化器,以使得该初始化器可以将它的类添加到某个List中。遗憾的是,静态初始化器只有在类首先被加载的情况下才能被调用,因此你就碰上了“先有鸡还是先有蛋”的问题:生成器在其列表中不包含这个类,因此它永远不能创建这个类的对象,而这个类也就不能被加载并置于这个列表中。
这主要是因为,你被强制要求自己去手工创建这个列表(除非你想编写一个工具,它可以全面搜索和分析源代码,然后创建和编译这个列表)。因此,你最佳的做法是:将这个列表置于一个位于中心的,位置明显的地方,而我们感兴趣的继承结构的基类可能就是这个最佳位置。
这里我们需要做的其他修改就是使用工厂方法设计模式,将对象的创建工作交给类自己去完成。工厂方法可以被多态的调用,从而为你创建恰当的对象。在下面这个非常简单的版本中,工厂方法就是Factory接口中的create()方法:
public interface Factory<T> {
T create();
}
泛型参数T使得create()可以在每种Factory实现中返回不同的类型。这也充分利用了协变返回类型。
在下面的示例中,基类Part包含了一个工厂对象的列表。对应这个由createRandom()方法产生的类型,它们的工厂都被添加到了partFactories List中,从而被注册到了基类中:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import typeinfo.factory.Factory;
class Part {
public String toString() {
return getClass().getSimpleName();
}
static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>();
static {
// Collections.addAll();
partFactories.add(new FuelFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAirFilter.Factory());
partFactories.add(new OilFilter.Factory());
partFactories.add(new FanBelt.Factory());
partFactories.add(new GeneratorBelt.Factory());
partFactories.add(new PowerSteeringBelt.Factory());
}
private static Random r = new Random();
public static Part createRandom() {
int n = r.nextInt(partFactories.size());
return partFactories.get(n).create();
}
}
/**
* 过滤器
*/
class Filter extends Part {
}
/**
* 燃油过滤器
*/
class FuelFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<FuelFilter> {
@Override
public FuelFilter create() {
return new FuelFilter();
}
}
}
/**
* 空气过滤器
*/
class AirFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<AirFilter> {
@Override
public AirFilter create() {
return new AirFilter();
}
}
}
/**
* 机舱空气过滤器
*/
class CabinAirFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<CabinAirFilter> {
@Override
public CabinAirFilter create() {
return new CabinAirFilter();
}
}
}
/**
* 机油过滤器
*/
class OilFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory<OilFilter> {
@Override
public OilFilter create() {
return new OilFilter();
}
}
}
/**
* 皮带
*/
class Belt extends Part {
}
/**
* 风扇传动皮带
*/
class FanBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<FanBelt> {
@Override
public FanBelt create() {
return new FanBelt();
}
}
}
/**
* 发动机皮带
*/
class GeneratorBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {
@Override
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
}
/**
* 动力转向带
*/
class PowerSteeringBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory<PowerSteeringBelt> {
@Override
public PowerSteeringBelt create() {
return new PowerSteeringBelt();
}
}
}
public class RegisteredFactories {
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
System.out.println(Part.createRandom());
}
}
并非所有在继承结构中的类都应该被实例化,在本例中,Filter和Belt只是分类标识,因此你不应该创建它们的实例,而只应该创建它们的子类的实例。如果某个类应该由createRandom()方法创建,那么它就包含一个内部Factory类。如上所示,重用名字Factory的唯一方式就是限定typeinfo.factory.Factory。
尽管你可以使用Collections.addAll()来向列表中添加工厂,但是这样做编译器就会表达它的不满,抛出一条有关“创建泛型数组”的警告,因此我转而使用add()。createRandom()方法从partFactories中随机地选取一个工厂对象,然后调用其create()方法,从而产生一个新的Part。
二、instanceof与Class的等价性
在查询类型信息时,以instanceof的形式(即以instanceof的形式或isInstance()的形式,它们产生相同的结果)与直接比较Class对象有一个很重要的差别。下面的例子展示了这种差别:
class Base {
}
class Derived extends Base {
}
public class FamilyVsExactType {
static void test(Object x) {
System.out.println("----Testing x of type " + x.getClass());
System.out.println("x instanceof Base " + (x instanceof Base));
System.out.println("x instanceof Derived " + (x instanceof Derived));
System.out.println("Base.isInstance(x) " + Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) " + Derived.class.isInstance(x));
System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class));
System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class));
System.out.println("x.getClass().equals(Base.class) " + (x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Derived.class) " + (x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
}
test()方法使用了两种形式的instanceof作为参数来执行类型检查。然后获取Class引用,并用==和equals()来检查Class对象是否相等。使人放心的是,instanceof和isInstance()生成的结果完全一样,equals()和==也一样。但是这两组测试得出的结论却不同。instanceof保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而如果用==比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是。
如果本文对您有很大的帮助,还请点赞关注一下。
- 点赞
- 收藏
- 关注作者
评论(0)