我需要输入方法签名,因此它接受2个不同特定具体子类型的同等类型参数。
是否可以使用泛型对此进行编码?你会如何解决它? (这个案子绝对是一个例子)
public <T extends List<?>> T<String> sum(T<Integer> sublistOfInts, T<Boolean> sublistOfBooleans){
/*fusion both lists*/
return sublistOfStrings;
}
编辑:最后,我正在寻找的是编译器通过的方式:
ArrayList<String> myList = sum(new ArrayList<Integer>(), new ArrayList<Boolean>());
但不是:
ArrayList<String> myList = sum(new ArrayList<Double>(), new ArrayList<Boolean>());
,也不
ArrayList<String> myList = sum(new LinkedList<Integer>(), new ArrayList<Boolean>());
(...)
编辑2:我找到了一个更好的例子。想象一个界面Tuple,有子类Duple,Triple&gt; ...,这样的东西会非常好看
<T extends Tuple<?>> T<String> reset( T<String> input, T<Boolean> listToNull){
T copy = input.copy();
for (int i=0; i<input.size();i++){
if (listToNull.get(i)){
copy.set(i,null);
}
}
}
答案 0 :(得分:4)
首先,摆脱方法参数泛型。当您想要返回ArrayList<Integer>
时,没有理由强制呼叫者提供ArrayList<Boolean>
和ArrayList<String>
。只需接受任何List<Integer>
和List<Boolean>
,然后将其留给您的方法,将其转换为相应的返回List
。
由于您知道要返回某种List
String
,因此您可以将参数编写为<T extends List<String>>
,将返回类型简称为T
。
这给我们留下了困难的部分:让你的方法实例化一个未知类型的对象。那很难。你不能只做new T();
。您需要调用代表您生成T
的内容。幸运的是,Java 8为Supplier<T>
提供了功能接口。您只需调用get()
方法即可获得ArrayList<String>
或其他任何您想要的内容。令人痛苦的部分是你的调用者需要提供他们自己的Supplier
。但我认为这与Java 8一样好。
以下是代码:
public <T extends List<String>> T sum(
List<Integer> sublistOfInts,
List<Boolean> sublistOfBooleans,
Supplier<T> listMaker) {
T sublistOfStrings = listMaker.get();
/*fusion of both lists*/
return sublistOfStrings;
}
至少这个编译:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, ArrayList::new);
这不是:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, LinkedListList::new);
您甚至可以在调用中省略type参数。这编译:
ArrayList<String> myNewList = thing.sum(intList, boolList, ArrayList::new);
这不是:
ArrayList<String> myNewList = thing.sum(intList, boolList, LinkedListList::new);
简而言之,这是因为类型参数本身不能参数化。那是因为我们不知道他们自己会采用多少类型的参数,也不知道可能对它们施加的限制。
选择相对模糊的班级RoleList
。它扩展了ArrayList<Object>
,因此符合List<?>
。但它根本不需要类型论证。因此,如果某人使用sum()
调用了您的RoleList
方法,则在您的示例中需要:
RoleList<Integer> intList = // something
RoleList<Boolean> boolList = // something
RoleList<String> myNewList = thing.sum(intList, boolList);
这显然无法工作,因为它需要一个非参数类型来获取类型参数。如果你像这样取下类型参数:
RoleList intList = // something
RoleList boolList = // something
RoleList myNewList = thing.sum(intList, boolList);
然后,您的方法需要能够接受两个List<Object>
参数并返回值List<Object>
。这违反了你的基本前提,你可以控制这些事情。
实际上,此处不应允许RoleList
,因为您无法保证一个实例仅包含Integer
s,另一个实例仅包含Boolean
s,以及仅次于String
。在这里允许RoleList
的编译器必然会有比我们现在更弱的类型检查。
所以最重要的是,你不能做你所要求的,因为Java不是那样构建的。
您仍然可以使用上面建议的方法在sum()
方法中获得完整的类型安全性。您确保传入的List
分别仅包含Integer
或Boolean
值。您确保调用者可以依赖于仅包含List
值的String
特定子类型的返回。所有有所作为的保证都存在。
答案 1 :(得分:2)
关于上述事情,有两件事让我感到震惊。你如何实例化sublistOfStrings
,你期望使用普通旧继承获得哪些优势?
有几种实例化T<String>
的方法。您可以让工厂检查您的参数类,并基于此实例化它。或者你可以这样做:
(List<String>)sublistOfInts.getClass().newInstance()
但你不能去new T<String>()
。因此,无论如何,你都要根据你的一个论点的类型来确定你的返回类型的实现(除非我没有想到这样的方式)。
通过指定两个参数的类型为&#39; T&#39;并不意味着他们完全属于同一种具体类型&#39; T&#39;无论是。例如
sum((int)1, (long)2L); // valid
sum((int)2, (double)2.0D); // valid ... etc
public <T extends Number> T sum(T a, T b) {
return a;
}
因此,您并未强制执行sublistOfInts
和sublistOfBooleans
类型为ArrayList
,因此您可以返回ArrayList
。您仍然需要编写代码来检查您希望根据参数返回的List<?>
类型。
我认为你最好不要使用泛型,并使用类似的东西:
public List<String> sum(List<Integer> sublistOfInts, List<Boolean> sublistOfBooleans) {
// Determine what subclass of list you want to instantiate based on `sublistOfInts` and `sublistOfBools`
// Call factory method or newInstance to instantiate it.
// Sum, and return.
}
您仍然可以使用List<?>
的子类型调用它。即使Java确实允许你这样做,我也不会相信你可以从泛型中获得任何优势(这是不可能的,因为它不能像T
那样参数化$cmd = self::$S3Client->getCommand('GetObject', array('Bucket' => USER_CONTENT_BUCKET_NAME,'Key' => $key));
$request = self::$S3Client->createPresignedRequest($cmd, '+20 minutes');
这一点)。
答案 2 :(得分:0)
我知道你拥有的只是一个例子,但是如果你只想返回一个包含一组其他列表中所有内容的String值的列表,你可以指定一个采用无限列表的varargs的方法
public List<String> sum(List<?>... lists) {
List<String> sublistOfStrings = new ArrayList<String>();
for(List<?> list : lists) {
for(Object obj : list) {
sublistOfStrings.add(obj.toString());
}
}
return sublistOfStrings;
}