提问者:小点点

我可以存储要intantiate的类型列表吗?


我想要一个可以通过索引创建其他类的实例的类。我想要一个包含所有可以实例化的类型(所有类型都扩展Object)的列表,这样我就可以有一个像public Object newObject(int i)这样的函数,它将返回该类型的新实例(它们每个都有一个默认构造函数)。

有没有办法存储类型列表?


共2个答案

匿名用户

您在这里有2个选项。

  • 工厂模式(推荐)
  • 反射式访问(不要去这里)

然而,第二种选择很常见。我将首先解释它,以便您知道应该避免什么,并将工厂解决方案放在上下文中。

类有一个对象表示:java. lang.Class类的一个实例。您可以轻松获取这些实例:Class

给定这样一个对象,您可以获取no-args构造函数并调用它:

Class<?> c = Class.forName("java.lang.String");
Constructor<?> noArgsConstructor = c.getConstructor();
Object fresh = noArgsConstructor.newInstance();
System.out.println("".equals(fresh)); // prints 'true'

然而,这样做有很多问题:

  • Class(和Constructor)的泛型被破坏了。泛型允许泛型-你可以有一个列表
interface Namable {
  void setName(String name);
}

public static <T extends Namable> T create(Class<T> type) {
  T namable = type.newConstructor().newInstance();
  namable.setName("Jane");
  return namable;
}

// to use:

@lombok.Data
class Person implements Namable {
  String name;
}

Person p = create(Person.class);
System.out.println(p.name); // prints 'Jane'

到目前为止,一切顺利。但现在让我们做同样的事情,但使用List而不是Namable

public <L extends List<String>> L create(Class<L> listType) {
  List<String> list = listType.getConstructor().newInstance();
  list.add("Hello, World!");
  return list;
}

这…编译很好,但您不能在没有很多问题和编译器警告的情况下使用它:

create(ArrayList<String>.class); // does not compile
create(ArrayList.class); // does, but warnings.

>

  • 这种反射式访问会引发大量异常。当然,您可以处理它们,但这很烦人。

    编译器和类型系统在这里对您没有帮助。作为“合同”的一部分,

    因此,这不是一个好主意。

    这一切如此不稳定的原因是静态成员没有“做”类型层次结构。构造函数只是一个不稳定的静态方法——想想吧:你不能在接口中声明需要某个构造函数——这与静态方法相同。它们也不做动态调度——就像静态方法一样。它们不需要接收者(x.foo()中的x称为接收者)——就像静态方法一样。

    您根本无法创建一个接口,该接口规定实现的任何类型都必须具有某些静态属性。一点也不。java作为一种语言根本不是这样工作的。只有实例才玩动态调度游戏,因此只有实例才有意义拥有一个允许您做出promise的类型层次结构(例如:“所有类型为List的东西都有一个. size()方法,而不管它是哪个特定的列表),因此,让工具(编译器、编辑器)提供帮助。

    所以,我们需要以某种方式将静态事物变成实例事物。

    输入:工厂模式。

    工厂只是第二个元层,与其说是描述“一棵树”,不如说是描述“了解某一属树并能制造新树的东西”:

    public interface Tree {
      void sprout();
    }
    
    class Birch implements Tree {
      public void sprout() { ... }
    }
    

    我们在此添加:

    public interface TreeFactory {
      Tree create();
    }
    
    public class BirchFactory implements TreeFactory {
      Birch create() { return new Birch(); }
    }
    

    我们现在已经“实例化”了曾经是静态方面的东西——根据“树”或“桦树”的概念,create()是“静态”——作为一个概念,它适用于所有这些方面。但是从“树制造商”的概念来看,create()是一个实例——每个树制造商都有自己的创建概念。

    使用这种模式,反射式访问的所有问题都消失了:

    >

  • 您可以使用该接口来判定您有例如没有参数。

    没有奇怪的检查异常要处理。

    适用于泛型。

    工厂的一种变体是隐式工厂——你也可以只传递一个函数来动态创建它们,而不是显式地创建一个TreeFactory对象。事实上,java核心库自己的Collectors. toMapCollection.toArray使用了这种技术:由于泛型,集合实际上不能创建一个正确类型的新数组,所以它需要你告诉它如何做到这一点。你可以用IntFunction来做到这一点

    List<String> list = new ArrayList<String>();
    list.toArray(size -> new String[size]);
    // or simpler:
    list.toArray(String[]::new);
    

    您可以将相同的技术应用于您的代码:

    public <T extends Tree> List<T> plantManyTrees(int size, Supplier<? extends T> treeFactory) {
      var planted = new ArrayList<T>();
      for (int i = 0; i < size; i++) {
        planted.add(treeFactory.get());
      }
      return planted;
    }
    
    // to use:
    
    List<Birch> copseOfBirches = plantManyTrees(100, () -> new Birch());
    

    或者,如果您想要,例如,基于字符串的动态查找树类型:

    Map<String, Supplier<? extends Tree>> makers = new HashMap<>();
    
    makers.put("Birch", () -> new Birch());
    makers.put("Willow", () -> new Willow());
    

    等等

  • 匿名用户

    您可以使用列表或类数组。祝你好运!