【发布时间】:2019-01-08 01:55:05
【问题描述】:
重要提示:
我目前拥有的代码符合我的期望。它做我想让它做的事情。我的问题是关于我让它工作的方式是否错误。我问这个的原因是因为我已经看到了大量关于原始类型的堆栈溢出结果以及它们基本上应该如何被使用。
我在做什么以及为什么使用原始类型
目前我正在动态创建一个通用接口的具体子类,其中接口在构造类时接受参数。当我创建这个类的一个实例并使用它返回的对象来调用各种方法时,我使用原始类型,因为它适用于我正在尝试做的事情。这是我的功能代码中使用原始类型的示例。此代码是自上而下的,即代码块之间没有代码。
加载属性文件
Properties prop = new Properties();
try {
prop.load(ObjectFactory.class.getResourceAsStream("config.properties"));
这是实现FileParserImplementation 并接收数据并将其放入数组的文件解析器。此代码获取 Class 类型,然后动态创建该类型的实例。
Class<? extends FileParserImplementation> parser = null;
parser = Class.forName(prop.getProperty("FileParserImplementation")).asSubclass(FileParserImplementation.class);
FileParserImplementation ParserInstance = (FileParserImplementation) parser.getDeclaredConstructors()[0].newInstance();
这两个类及其实例是实现DataParserImplementation 的两个独立的DataParsers。这些接收FileParser 提供的Strings 数组,并创建对象/将数据处理为所需的任何内容。它列出了这些数据的集合。 Fileparser 依赖是通过构造函数注入传入的。这可以在运行时通过属性文件进行配置。
Class<? extends DataParserImplementation> dataset1 = Class.forName(prop.getProperty("DataParserImplementation_1")).asSubclass(DataParserImplementation.class);
Class<? extends DataParserImplementation> dataset2 = Class.forName(prop.getProperty("DataParserImplementation_2")).asSubclass(DataParserImplementation.class);
DataParserImplementation Dataset1Instance = (DataParserImplementation) dataset1.getDeclaredConstructors()[0].newInstance(ParserInstance);
DataParserImplementation Dataset2Instance = (DataParserImplementation) dataset2.getDeclaredConstructors()[0].newInstance(ParserInstance);
这是实现CrossReferencerImplementation 的Crossreferencer 类。它接收两个数据集并以实际具体反射类所需的任何方式交叉引用它们。这也可以在运行时进行配置。它在这个 main 中输出一个 Map。
该地图作为数据的最终集合(我以后可能会更改)。
Class<? extends CrossReferenceImplementation> crossreferencer = Class.forName(prop.getProperty("CrossReferenceImplementation")).asSubclass(CrossReferenceImplementation.class);
CrossReferenceImplementation crossReferencerInstance =
(CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
通过调用反射实例上的方法来获取 Map 结果。然后打印出这张地图的内容。目前似乎地图参数也得到了,因为地图内的对象在调用reflectiveFinalMap.get(key).toString() 时正确使用了它们的 toString 方法。
这让我相信它可以按我的意愿工作。
Map reflectiveFinalMap = (Map)
crossReferencerInstance.CrossReference(Dataset1Instance.Parse(), Dataset2Instance.Parse());
for (Object key:reflectiveFinalMap.keySet()) {
System.out.println(key + " { " +
reflectiveFinalMap.get(key).toString() + " }");
}
return reflectiveFinalMap;
}
//catch block goes here
请注意,每次我反思性地创建一个实现我的某个接口的类的实例时,我都会使用该接口作为原始类型。我的希望是,反射在创建具体子类时会看到这个原始类型的参数化类型,因为那是实际指定参数类型的地方。关键是让任何实现这些接口的类都是通用的,以至于它们可以接受几乎任何东西并返回几乎任何东西。
我试图不使用原始类型的事情。
我已经尝试在反射的crossreferencer Class 中实际获取CrossReferenceImplementation 的参数化类型,我现在通过调用
Class arrayparametertype = (Class)((ParameterizedType)crossreferencer.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
然后我尝试在创建crossreferencer 的实例时传递arrayparameter,如下所示:
CrossReferenceImplementation crossReferencer = (CrossReferenceImplementation<<arrayparametertype>>) crossreferencer.getDeclaredConstructors()[0].newInstance();
这不起作用,因为可变参数类型显然不是一个东西。 我试图手动指定具体反射类的特定参数(无论如何我都不想要这个,因为它破坏了这里的整个反射点,通过能够使用实现适当接口的任何东西来将类彼此解耦)。这导致出现此警告并且代码实际上没有运行它应该运行的方法:
//how the parameters were specified. Messy and breaks the reflection.
CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> crossReferencer = (CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
//where the warning occured
Map reflectiveFinalMap = (Map) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
警告:
“Dataset1 具有原始类型,因此 Parse 的结果被擦除”。
请注意,此处的SalesRep 是将数据作为该对象的字段保存在其中的对象。该对象被操纵并放入各种集合中。在DataParserImplementations的许多方法中也可以通过反射访问它
在指定 Map 的参数类型时出现了类似的错误消息和问题(我不希望这样做,因为它使反射毫无意义,我希望 map 返回结果是通用的并由实现类指定)。
//where the parameterized type was specified
Map reflectiveFinalMap = (Map<String,SalesRep>) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
当指定映射结果的实际参数化类型时,错误消息是:
“crossReferencer 具有原始类型,因此 CrossReference 的结果被删除”。
运行代码确实让我确认.CrossReference 方法的结果已被删除,而其他一切运行正常。
在这里询问之前我尝试了哪些互联网搜索
所以我对这两个操作都使用了原始类型,从主代码中可以看出,一切正常。但是我已经看到了很多“不要使用原始类型”。这就是为什么我要问:这是对原始类型的适当使用吗?我应该以不破坏反射的不同方式来做吗?它打破了反射,因为手动指定类型参数不仅使我的代码无法运行,还意味着只能使用具体类。我反映以便我可以使用任何实现通用接口的东西。我不想只能使用特定的具体实例。我尝试在堆栈溢出中搜索我的标题和其他类似内容。我认为这可能与类型擦除有关,但老实说我不确定。没有其他东西真正解决了这个问题,因为没有人同时谈论泛型、参数化类型和反射(我的问题的症结所在)。有人告诉我泛型和反射不能很好地协同工作,但是这段代码无论如何都可以工作,并且可以按照我想要的方式工作。它运作良好。我只是想确保我没有做错什么。
目标。
为了了解我当前对原始类型的使用情况,以便我知道我的做法是正确的。 “正确”的意思与我在下面定义的“错误”方式相反。我寻求的“理解”的一个例子是:
要理解为什么 puesdo 代码如下:
ConcreteClass forname(myPropertiesFileObject.get(ConcreteClassname)) as subClass of (MyGenericInterface);
MyRAWGenericInterfaceType ConcreteClassInstance = (MyRAWGenericInterfaceType) ConcreteClass.newInstance( Insert generic Type constructor arguments here);
RAWCollectionType someCollection = RAWCollectionType concreteClassInstance.CallingAMethod(Insert generic Type method arguments here);
使用原始类型,其中RAW 包含在接口或集合类型名称中。这与以某种不使用原始类型但不破坏反射点的方式相反,以解耦这些类之间的交互。在这种情况下,使用硬代码指定参数会“破坏反射”。另外,我想了解为什么在上面的 pusdocode 中为这些 RAW 类型指定参数(即使我知道那不是我要做的)会导致问题中上面列出的错误,即为什么是结果向方法返回的RAWCollectionType 提供实际参数时,CallingAMethod 被擦除?根本问题是,当我在声明时向RAWCollectionType 提供类型参数时,它拒绝被CallingAMethod 返回的内容更新,我不明白为什么。它接受返回值,但如果 CallingAMethod 方法的主体将返回值作为参数传入,在方法内部更新然后返回,则我收到的返回没有更新。 CallingAMethod 在这个例子中就像我有一个类似的列表:
[1,2,3]
在方法内部我有类似的东西:
foreach(thing in list){
thing += 1
}
然后我返回了列表,指定参数时得到的返回值为 [1,2,3],而使用原始类型时,返回值为 [2,3,4],如我所愿。我问这个是因为我听说过关于使用原始类型的坏事。
此外,我想确保我对原始类型的使用没有严重错误,并且它可以正常工作,因为它应该可以工作。也许我刚刚精通整个反射和泛型,并找到了原始类型的有效用途,或者我可能正在做一些可怕的事情,以至于我需要逮捕。这就是我打算找出的。为了澄清,我的意思是错误的:
糟糕的设计(应该使用不同的方式来反射性地调用我的方法,并使用使用泛型接口的反射类)
低效设计(时间复杂度、代码行或可维护性)
有更好的方法,你甚至不应该首先这样做
如果您在阅读此代码时出现任何这些原因或我错过的任何内容,请告诉我。否则请解释为什么我使用原始类型是有效的并且不违反这个问题:[链接]What is a raw type and why shouldn't we use it?
【问题讨论】:
-
你能发布你想要实现的伪代码吗?因为我仍然不明白你的目标。只是一些不是有效的 java 代码,但您希望它能够正常工作。请注意,Java 中的泛型类型主要是编译器端的(请参阅类型擦除),因此运行时的 Map 无论如何只是一个 Map,并且反射是动态的,因此它们总是只对 Object 进行操作。您只能编写一些实用程序,将字段/方法变形为一些通用接口,如
FieldAccessor<T>- 但您仍然需要使用不安全的强制转换来做到这一点。 (但无论如何你都可以在运行时使其类型安全) -
@GotoFinal 更新了我的问题以包含问题关键的伪代码。它包含在“目标”标题的正下方。不幸的是,我没有太多具体的代码目标,因为我已经实现了我的目标,我只需要更好地了解我是如何做到的,这样我就知道我将来要做什么。 “理解”在问题中的定义更好,因为在没有具体要理解的情况下仅仅要求理解就太宽泛了。
标签: java generics reflection interface raw-types