[英]Generics: Type as variable?
为了能够替代特定的实现,通常知道写
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
为什么不使用某种工厂模式而不是直接实例化特定的列表实现。 然后,您需要在工厂方法内的一个位置更改列表实现。
例如,您从以下开始:
List<T> createList() {
return new ArrayList<T>();
}
List<T> myList1 = createList();
List<T> myList2 = createList();
稍后,如果您决定需要链接列表,则只需更改createList()的实现,其余代码保持不变。
List<T> createList() {
return new LinkedList<T>();
}
嗯 - 据我了解你的问题,你想做的事情已经成为可能(与泛型无关)。
在实际进行构造函数调用时,您总是需要给出列表(或任何类)的确切类型,这是不可避免的。 同样,您始终可以避免在其他任何地方使用特定内容(将其存储在变量中,从方法返回,将其作为方法参数传递等)。 在你的情况下,你已经这样做 - 通过将myList
声明为List,如果你改变你存储在其中的具体类列表,你不需要改变它的声明类型。
我认为你的问题可能是因为你在同一个类中创建了两个不同的列表(但是它们都是相同的类型),并且你想要抽象出来。 您可以使用工厂类型模式轻松完成此操作; 或者使用单独的工厂,或者在您的情况下,只需用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();
}
}
但是不要这样做 - 它是缓慢的,不灵活的,不寻常的(对于其他开发人员来说太难了)在这种情况下完全没有必要......
双重编辑:哦,你必须处理一大堆基于反射的检查异常,我将这些异常作为练习留给读者,以防你感到受到诱惑。 ;-)
如果我现在想用LinkedList而不是ArrayList替换实现怎么办? 它需要在两个位置上进行更改。
不,你不会。 您可以单独更改每个位置。 即,其中一个分配可以是LinkedList,另一个可以是ArrayList。
这实际上与泛型无关,而是与多态性有关 。
使用List
(或List<T>
)而不是ArrayList
(或ArrayList<T>
)的关键是List
是一个接口,而ArrayList
是一个具体的实现。 接口很好,您应该使用它们,但它们实际上与泛型无关。
在您的示例中,为什么需要将List
的实际类型变为变量? 如果你真的想抽象出对象的创建,你应该使用一个工厂方法,就像你正在做的那样。
一般来说,一般多态性和特定接口的目的是数据对象的客户端 (“使用者”)不需要知道实现细节。 但是, 创建 (“生成”)对象的代码应该能够知道实现细节(因为它填充了对象)。 因此,让对象创建代码知道它正在创建ArrayList
或LinkedList
或其他任何东西都应该没有问题。
实例化“参数化”List实现的唯一方法是通过反射,但是你肯定是通过返回List接口而不是复杂类来做正确的第一步。 一些可行的想法:
如果您希望客户端选择List实现:在构造函数中接受List类:
public MyClass(Class <?extends List <T >>){...}
想法#2需要反射来实例化List实现,但这可能不是你想要的。
如果列表中的“数组”或“链接”部分对您的实现很重要,那么将其公开。 如果不重要,那就不要了。
你可以有一个像这样的界面:
ArrayList<T> createArrayList();
如果由于某种原因,List的实施很重要。 当您需要的只是List-ishness时,返回“List”是一种很好的做法,在这种情况下,实现不太重要。
他们我理解你的问题(我可能会误解;你的问题不是很清楚),你只是在搜索通用方法的正确语法 - 它看起来像这样:
public <T> List<T> getSpecificList()
{
return new ArrayList<T>();
}
- 注意前导<T>
。 现在,如果要更改列表的类型,则此更改仅限于一个位置。
如果您没有初始化myList,那么您只需要在一个位置更改内容。 除非您当然需要使用ArrayList独有的任何方法...
List<AnyType> myList = getSpecicList();
public List<AnyType> getSpecificList()
{
return new ArrayList<AnyType>();
}
这个怎么样?
public class MyClass<T>
{
List<T> myList = this.getSpecificList();
public List<T> getSpecificList()
{
return new ArrayList<T>();
}
}
现在您只需要在一个地方更改类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.