简体   繁体   English

泛型:输入变量?

[英]Generics: Type as variable?

In order to be able to substitute a specific implementation, it is commonly known to write 为了能够替代特定的实现,通常知道写

List<AnyType> myList = new ArrayList<AnyType>();

instead of 代替

ArrayList<AnyType> myList = new ArrayList<AnyType>();

This is easy to understand, this way you might change the implementation from ArrayList to LinkedList or any other kind of List with ease. 这很容易理解,这样您可以轻松地将实现从ArrayList更改为LinkedList或任何其他类型的List。

Well... this is all good and nice, but as I cannot instanciate "List" directly, I therefore would be required to type 嗯......这一切都很好,但由于我不能直接实例化“List”,因此我需要输入

public List<AnyType> getSpecificList()
{
    return new ArrayList<AnyType>();
}

which makes the previous pattern quite senseless. 这使得以前的模式毫无意义。 What if I now want to replace the implementation by an LinkedList instead of an ArrayList? 如果我现在想用LinkedList而不是ArrayList替换实现怎么办? It would be required to change it on two positions. 它需要在两个位置上进行更改。

Is it possible to have something like this (I know the syntax is absolutely incorrect)? 有可能有这样的东西(我知道语法绝对不正确)?

public class MyClass<T>
{
    Type myListImplementation = ArrayList;

    List<T> myList = new myListImplementation<T>();

    public List<T> getSpecificList()
    {
        return new myListImplementation<T>();
    }
}

This would allow me to simply change the word "ArrayList" to "LinkedList" and everything is fine. 这将允许我简单地将单词“ArrayList”更改为“LinkedList”,一切都很好。 I know that both lists may have different constructors and this would not work "as is". 我知道这两个列表可能有不同的构造函数,这不会“按原样”工作。 And I don't really want to add a second type-parameter for specifying the list-implementation that is being used. 我真的不想添加第二个类型参数来指定正在使用的列表实现。

Is there any clean mechanism to fix this?^ 是否有任何干净的机制来解决这个问题?^

Thanks in advance and best regards Atmocreations 在此先感谢和最好的问候Atmocreations

Why not use some kind of factory pattern instead of instantiating a specific list implementation directly. 为什么不使用某种工厂模式而不是直接实例化特定的列表实现。 Then you need to change list implementation only in one place, inside the factory method. 然后,您需要在工厂方法内的一个位置更改列表实现。

For example you start with: 例如,您从以下开始:

 List<T> createList() {
     return new ArrayList<T>();
 } 

 List<T> myList1 = createList();
 List<T> myList2 = createList();

Later, if you decide that you need a linked list instead, you just change the implementation of createList() and the rest of your code stays the same. 稍后,如果您决定需要链接列表,则只需更改createList()的实现,其余代码保持不变。

List<T> createList() {
    return new LinkedList<T>();
}

Erm - as far as I understand your question, what you want to do is already possible (and nothing to do with generics). 嗯 - 据我了解你的问题,你想做的事情已经成为可能(与泛型无关)。

You will always need to give the exact type of the list (or any class) when actually making a constructor call, that's unavoidable. 在实际进行构造函数调用时,您总是需要给出列表(或任何类)的确切类型,这是不可避免的。 Likewise, you can always avoid specifics everywhere else (storing it in a variable, returning it from a method, passing it as a method parameter, etc.). 同样,您始终可以避免在其他任何地方使用特定内容(将其存储在变量中,从方法返回,将其作为方法参数传递等)。 In your case you're already doing this - by declaring myList as simply a List, you don't need to change it's declared type if you change the concrete class of list you store in it. 在你的情况下,你已经这样做 - 通过将myList声明为List,如果你改变你存储在其中的具体类列表,你不需要改变它的声明类型。

I think your question may be around the fact that you're creating two different lists in the same class (but both of the same type), and you want to abstract this out. 我认为你的问题可能是因为你在同一个类中创建了两个不同的列表(但是它们都是相同的类型),并且你想要抽象出来。 You can do this quite easily with a factory-type pattern; 您可以使用工厂类型模式轻松完成此操作; either with a separate factory, or in your case, just replacing the myList declaration with 或者使用单独的工厂,或者在您的情况下,只需用myList声明替换

List<T> myList = getSpecificList();

Edit - out of interest, the closest thing you could get to your original proposed fix would be using reflection: 编辑 - 出于兴趣,你可以得到最接近原始建议修复的东西是使用反射:

public class MyClass<T>
{
    Class<? extends List<T>> myListClass = ArrayList.class;

    List<T> myList = myListClass.newInstance();

    public List<T> getSpecificList()
    {
        return myListClass.newInstance();
    }
}

But don't do this - it's slow, inflexible, unusual (so harder to grok for other developers) and completely unnecessary in this case... 但是不要这样做 - 它是缓慢的,不灵活的,不寻常的(对于其他开发人员来说太难了)在这种情况下完全没有必要......

Double-edit: oh, and you'd have to deal with a whole bunch of reflection-based checked exceptions that I've left as an exercise to the reader, just in case you felt tempted. 双重编辑:哦,你必须处理一大堆基于反射的检查异常,我将这些异常作为练习留给读者,以防你感到受到诱惑。 ;-) ;-)

What if I now want to replace the implementation by an LinkedList instead of an ArrayList? 如果我现在想用LinkedList而不是ArrayList替换实现怎么办? It would be required to change it on two positions. 它需要在两个位置上进行更改。

No, you wouldn't. 不,你不会。 You could change each location independently. 您可以单独更改每个位置。 ie, one of those allocations could be a LinkedList and the other could be an ArrayList. 即,其中一个分配可以是LinkedList,另一个可以是ArrayList。

This really has nothing to do with generics, but is instead having to do with polymorphism . 这实际上与泛型无关,而是与多态性有关

The point of using List (or List<T> ) instead of ArrayList (or ArrayList<T> ) is that List is an interface while ArrayList is a concrete implementation. 使用List (或List<T> )而不是ArrayList (或ArrayList<T> )的关键是List是一个接口,ArrayList是一个具体的实现。 Interfaces are good, and you should use them, but they really have nothing to do with generics directly. 接口很好,您应该使用它们,但它们实际上与泛型无关。

In your example, why do you need to make the actual type of your List a variable? 在您的示例中,为什么需要将List的实际类型变为变量? If you really want to abstract away the creation of the object, you should use a factory method, as it appears you're doing. 如果你真的想抽象出对象的创建,你应该使用一个工厂方法,就像你正在做的那样。

Generally speaking, the purpose of polymorphism in general and interfaces in specific is that clients ("consumers") of your data objects don't need to know implementation details. 一般来说,一般多态性和特定接口的目的是数据对象的客户端 (“使用者”)不需要知道实现细节。 However, the code that creates ("produces") your objects should be in a position to know implementation details (since it's populating the object). 但是, 创建 (“生成”)对象的代码应该能够知道实现细节(因为它填充了对象)。 So it shouldn't be problematic to have the object creation code know that it's creating an ArrayList or a LinkedList or whatever. 因此,让对象创建代码知道它正在创建ArrayListLinkedList或其他任何东西都应该没有问题。

The only way to instantiate a "parameterized" List implementation would be through reflection, but you're definitely making the right first step, by returning the List interface rather than a complex class. 实例化“参数化”List实现的唯一方法是通过反射,但是你肯定是通过返回List接口而不是复杂类来做正确的第一步。 Some ideas that could work: 一些可行的想法:

  1. write a private method like newList() which just returns an empty List implementation. 编写一个像newList()这样的私有方法,它只返回一个空的List实现。 Use this throughout your class and then if you want to change it from ArrayList to LinkedList, you only have to change the implementation of that newList() method. 在整个类中使用它,然后如果要将它从ArrayList更改为LinkedList,则只需更改该newList()方法的实现。 This is similar to your getSpecificList() method 这与您的getSpecificList()方法类似
  2. If you want clients to choose the List implementation: accept a List class in the constructor: 如果您希望客户端选择List实现:在构造函数中接受List类:

    public MyClass(Class<? extends List<T>>) { ... } public MyClass(Class <?extends List <T >>){...}

Idea #2 would require reflection to instantiate the List implementation, however, which probably isn't what you want. 想法#2需要反射来实例化List实现,但这可能不是你想要的。

If the "Array" or "Linked" part of the list is important to your implementation, then expose it. 如果列表中的“数组”或“链接”部分对您的实现很重要,那么将其公开。 If it isn't important, then don't. 如果不重要,那就不要了。

You could have an interface like: 可以有一个像这样的界面:

 ArrayList<T> createArrayList();

if for some reason the implementation of List were important. 如果由于某种原因,List的实施很重要。 Returning "List" is good practice when all you need is the List-ishness, in which case implementation is less important. 当您需要的只是List-ishness时,返回“List”是一种很好的做法,在这种情况下,实现不太重要。

They way I understand your question (I could be mistaken though; your question isn't very clear), you are simply searching the correct syntax for a generic method – which simply looks like this: 他们理解你的问题(我可能会误解;你的问题不是很清楚),你只是在搜索通用方法的正确语法 - 它看起来像这样:

public <T> List<T> getSpecificList()
{
    return new ArrayList<T>();
}

– Notice the leading <T> . - 注意前导<T> Now if you want to change the type of the list, this change is restricted to one single position. 现在,如果要更改列表的类型,则此更改仅限于一个位置。

If you don't initialize myList, then you only need to change things in one spot. 如果您没有初始化myList,那么您只需要在一个位置更改内容。 Unless of course you need to use any of the methods unique to ArrayList... 除非您当然需要使用ArrayList独有的任何方法...

List<AnyType> myList = getSpecicList();

public List<AnyType> getSpecificList()
{
    return new ArrayList<AnyType>();
}

How about this? 这个怎么样?

public class MyClass<T>
{
    List<T> myList = this.getSpecificList();

    public List<T> getSpecificList()
    {
        return new ArrayList<T>();
    }
}

Now you only have to change the type in one place. 现在您只需要在一个地方更改类型。

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

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