繁体   English   中英

Java泛型-“ extends”关键字->不支持添加元素

[英]java generics - “extends” keyword -> not supporting add elements

由于Java泛型“ extend”关键字,我陷入了一个奇怪的问题。 我已经开发了一种通用方法,可以从一种通用方法中获取元素。

当我使用<? extends X> <? 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);

解决这个问题现在看起来非常具有挑战性。如果有人可以在这里提供一些提示,我将很高兴。

谢谢!!

如果您可以接受任何从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;
}

...或在本地尝试/捕获异常。

您可以通过使调用者传递所需类型的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);

综上所述,这是一个经过验证的有效实现,并使用在线java编译器进行检查:

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();
        }
    }
}

我宁愿说答案取决于边界条件。 我们可以编写代码来获得必要的功能,但它们可能/可能不遵守各种边界条件,例如性能,安全性,完整性等。

例如:以下代码

**T newInstance = targetClass.newInstance();**
**list.add(newInstance);**

也可以实现必要的功能,但可能/可能不遵守性能边界,因为对反射方法的任何调用都会检查安全性。

上面发布了带有Supplier的解决方案也实现了类似的功能,但它可能不遵守安全边界,因为恶意的Supplier可能会提供可能导致缓冲区溢出或DOS攻击的值。 (如果您真的想尝试一下,则可以创建一个静态供应商并使用self进行初始化。您将得到堆栈溢出。java泛型书还提供了与安全性相关的类似示例,您可以参考该示例)。 我看到的问题在于当前情况下的java open子类型。

还有其他解决方案可以实现可能/可能不遵守这些边界条件的所需功能(对创建的子类型的方式稍加修改)。 我看到的问题是由于开放式子类型系统引起的,这使得开发人员编写遵循所有边界条件的代码变得冗长且困难。

该解决方案还完全取决于您的模型结构,无论您是创建数据的子类型coz还是基于行为,这在此简短示例中可能都没有体现。

您可以参考Philiph Wadler的Java Generics Book,以获得有关我建议使用的泛型的更多详细信息。 它还提供了以安全方式编写代码的重要方面。 有趣的是,您可以参考有关Java数据类的项目amber(java 11或更高版本),它试图解决其中的一些问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM