由于java泛型而导致我遇到一个奇怪的问题"扩展"关键词。我开发了一种通用方法,使方法中的元素尽可能通用。
当我使用<? extends X>
时,我无法添加任何元素。
在我的情况下,我使用通用模板来限制用户提供的参数并提供返回类型ac。
class Root{
}
class Sub_1 extends Root{
}
class Sub_2 extends Root{
}
public static <T extends Root> List<T> getSubElements(Class<T> targetClass){
List<T> ls=new ArrayList<>();
if(targetClass.getSimpleName().equals("Sub_1")){
Sub_1 sub_1 = new Sub_1();
ls.add(sub_1);
}else if(targetClass.getSimpleName().equals("Sub_2")){
Sub_2 sub_2=new Sub_2();
ls.add(sub_2);
}
return ls;
}
在上面的例子中,当我向列表中添加元素时,我收到了编译错误。
ls.add(sub_1);
ls.add(sub_2);
现在解决这个问题看起来非常具有挑战性。如果有人能提供一些提示,我会很高兴。
谢谢!
答案 0 :(得分:2)
如果你可以接受从Root派生的任何类,并且都有一个默认的构造函数......
public static <T extends Root> List<T> getSubElements(Class<T> targetClass) throws ReflectiveOperationException {
List<T> ls = new ArrayList<>();
T t = targetClass.getDeclaredConstructor().newInstance();
ls.add(t);
return ls;
}
...或在本地尝试/捕获异常。
答案 1 :(得分:2)
您可以通过类型安全的方式执行此操作,而不使用反射,方法是让调用者传入所需类型的Supplier
而不是该类型的Class
。然后getSubElement
代码只是调用供应商来获取正确的实例:
static <T extends Root> List<T> getSubElements(Supplier<T> s) {
List<T> ls = new ArrayList<>();
ls.add(s.get());
return ls;
}
调用者需要提供一种方法来创建其所需子类的实例。这可能是使用构造函数引用,也可能是对静态工厂方法的引用。如果类层次结构是这样的:
public class Root { }
public class Sub1 extends Root {
public Sub1() { ... }
}
public class Sub2 extends Root {
public static Sub2 instance() { ... }
}
然后调用者可以编写如下代码:
List<Sub1> list1 = getSubElements(Sub1::new);
List<Sub2> list2 = getSubElements(Sub2::instance);
答案 2 :(得分:1)
总结一下,这是一个经过验证的工作实现,使用an online java compiler检查:
import java.util.*;
class Root{
}
class Sub_1 extends Root{
}
class Sub_2 extends Root{
}
public class Bla {
public static <T extends Root> T factoryMethod(Class<T> targetClass) throws Exception{
if (Sub_1.class ==(targetClass)) {
return (T) (new Sub_1());
}
else if (Sub_2.class == (targetClass)) {
return (T) (new Sub_2());
}
else {
throw new Exception("Unsupported class type");
}
}
public static List<Root> getSubElements() throws Exception{
List<Root> ls=new ArrayList<>();
ls.add(factoryMethod(Sub_1.class));
ls.add(factoryMethod(Sub_2.class));
return ls;
}
public static void main(String[] args) {
try {
List<Root> root = getSubElements();
System.out.println(root);
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 3 :(得分:0)
我宁愿说答案取决于边界条件。我们可以编写代码来获得必要的功能,但它们可能/可能不会遵循各种边界条件,如性能,安全性,完整性等等。
e.g: 以下代码
**T newInstance = targetClass.newInstance();**
**list.add(newInstance);**
也可以实现必要的功能,但可能/可能不符合性能边界,因为任何对反射方法的调用都会检查安全访问。
上面发布的供应商解决方案也实现了类似的功能,但它可能不符合安全边界,因为恶意供应商可以提供可能导致缓冲区溢出或DOS攻击的值。 (如果你真的想尝试一下,你可以创建一个静态供应商并用self初始化..你会得到一个堆栈溢出.java泛型书也提供了一个类似的安全相关的例子,你可以参考)。我看到的问题在于当前场景中的java open子类型。
还有其他解决方案也可以实现所需的功能(对创建的子类型的方式稍作修改),这可能会/可能不会遵循这些边界条件。我看到的问题是由于开放的子类型系统,并使开发人员编写符合所有边界条件的代码变得冗长和困难。
解决方案还完全取决于您的模型结构,无论您是创建了数据的子类型,还是基于行为,这可能不会反映在这个简短的示例中。
您可以参考Philiph Wadler编写的Java Generics Book,了解有关使用泛型的更多详细信息,我建议您这样做。它还提供了以安全的方式编写代码的重要方面。有趣的是,你可以参考项目琥珀(java 11或更高版本)关于java的数据类,它试图解决其中的一些问题。