类型信息instanceof与Class的等价性

举报
xcc-2022 发表于 2022/07/22 11:21:54 2022/07/22
【摘要】 一、注册工厂    生成Pet继承结构中的对象存在着一个问题,即每次向该继承结构添加新的Pet类型时,必须将其添加为LiteralPetCreator.java中的项。如果在系统中已经存在了继承结构的常规的基础,然后在其上要添加更多的类,那么就有可能会出现问题。    你可能会考虑在每个子类中添加静态初始化器,以使得该初始化器可以将它的类添加到某个List中。遗憾的是,静态初始化器只有在类首...

一、注册工厂

    生成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对象,就没有考虑继承——它或者是这个确切的类型,或者不是。

如果本文对您有很大的帮助,还请点赞关注一下。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。