泛型:输入变量?

时间:2009-10-08 16:42:03

标签: java generics

为了能够替代特定的实现,通常知道写

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

而不是

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

这很容易理解,这样您就可以轻松地将实现从ArrayList更改为LinkedList或任何其他类型的List。

嗯......这一切都很好,但由于我不能直接实例化“List”,因此我需要输入

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

这使得之前的模式毫无意义。如果我现在想用LinkedList而不是ArrayList替换实现怎么办?需要在两个位置进行更改。

是否有可能有这样的东西(我知道语法绝对不正确)?

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

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

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

这样我就可以简单地将“ArrayList”改为“LinkedList”,一切都很好。我知道这两个列表可能有不同的构造函数,这不会“按原样”工作。我真的不想添加第二个类型参数来指定正在使用的列表实现。

是否有任何干净的机制来解决这个问题?^

提前致谢并致以最诚挚的问候 Atmocreations

9 个答案:

答案 0 :(得分:14)

为什么不使用某种factory pattern而不是直接实例化特定的列表实现。然后,您需要在工厂方法内的一个地方更改列表实现。

例如,您从:

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

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

稍后,如果您决定需要链接列表,则只需更改createList()的实现,其余代码保持不变。

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

答案 1 :(得分:8)

嗯 - 就我理解你的问题而言,你想做的事情已经成为可能(而且与泛型无关)。

在实际进行构造函数调用时,总是需要提供列表(或任何类)的确切类型,这是不可避免的。同样,您始终可以避免在其他任何地方使用特定内容(将其存储在变量中,从方法返回,将其作为方法参数传递等)。在您的情况下,您已经这样做了 - 通过将myList声明为List,如果您更改存储在其中的具体类列表,则无需更改其声明类型。

我认为你的问题可能是因为你在同一个类中创建了两个不同的列表(但两者都是相同的类型),并且你想要抽象出来。您可以使用工厂类型模式轻松完成此操作;或者使用单独的工厂,或者在您的情况下,只需用{/ p>替换myList声明

List<T> myList = getSpecificList();

编辑 - 出于兴趣,您最接近原始建议修复的方法是使用反射:

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

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

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

但是不要这样做 - 它很慢,不灵活,不寻常(对于其他开发人员来说更难以理解)并且在这种情况下完全没有必要......

双重编辑:哦,你必须处理一大堆基于反射的检查异常,我将这些异常作为练习留给读者,以防你感到受到诱惑。 ; - )

答案 2 :(得分:5)

  

如果我现在想要更换   LinkedList实现   而不是ArrayList?这将是   需要在两个上更改它   位置。

不,你不会。您可以单独更改每个位置。即,其中一个分配可以是LinkedList,另一个可以是ArrayList。

答案 3 :(得分:3)

这与泛型无关,而是与polymorphism有关。

使用List(或List<T>)代替ArrayList(或ArrayList<T>)的重点是List界面ArrayList是一个具体的实现。接口很好,你应该使用它们,但它们实际上与泛型无关。

在您的示例中,为什么需要将List变量的实际类型设为?如果你真的想抽象出对象的创建,你应该使用一个工厂方法,就像你正在做的那样。

一般来说,一般多态性和特定接口的目的是数据对象的 clients (“consumer”)不需要知道实现细节。但是,创建(“生成”)对象的代码应该能够知道实现细节(因为它填充了对象)。因此,让对象创建代码知道它正在创建ArrayListLinkedList或其他任何内容应该没有问题。

答案 4 :(得分:0)

实例化“参数化”List实现的唯一方法是通过反射,但是你肯定是通过返回List接口而不是复杂类来做正确的第一步。一些可行的想法:

  1. 编写一个像newList()这样的私有方法,它只返回一个空的List实现。在整个类中使用它,然后如果要将它从ArrayList更改为LinkedList,则只需更改该newList()方法的实现。这与您的getSpecificList()方法类似
  2. 如果您希望客户端选择List实现:在构造函数中接受List类:

    public MyClass(Class&lt;?extends List&lt; T&gt;&gt;){...}

  3. 想法#2需要反射才能实例化List实现,但这可能不是你想要的。

答案 5 :(得分:0)

如果列表中的“数组”或“链接”部分对您的实现很重要,那么请将其公开。如果不重要,那就不要。

可以拥有如下界面:

 ArrayList<T> createArrayList();

如果出于某种原因,List的实施很重要。当您需要的只是List-ishness时,返回“List”是一种很好的做法,在这种情况下,实现不那么重要。

答案 6 :(得分:0)

他们的方式 了解你的问题(我可能会弄错;你的问题不是很清楚),你只是在搜索通用方法的正确语法 - 看起来像这样:

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

- 注意领先的<T>。现在,如果您想要更改列表的类型,则此更改仅限于一个位置。

答案 7 :(得分:0)

如果您没有初始化myList,那么您只需要在一个位置更改内容。除非你当然需要使用ArrayList独有的任何方法......

List<AnyType> myList = getSpecicList();

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

答案 8 :(得分:0)

这个怎么样?

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

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

现在你只需要在一个地方改变类型。