2016-08-05 36 views
2

我想学习Java中泛型编程的概念,但我被困在一个question.Consider功能最多的这些实现:泛型在Java中

1)

public static <T extends Comparable<? super T>> T max(Collection<? extends T> collection){ 
    //code 
} 

2)

public static <T extends Comparable<T>> T max(Collection<T> collection){ 
//code 
} 

我很好奇知道,他们之间有什么区别?

当然,我知道,声明Collection<? extends T>允许将T的子类型作为Collection传递,但在这种情况下,在静态方法中它的用法是什么?

结果必须是相同的,没有使用边界,不是吗?

那么这个<T extends Comparable<? super T>>呢?

如果你传递T的子类型,无论如何它都可以,因为T实现了Comparable并且Comparable的实现将保持在子类型中,所以你可以使用2)选项。

我只是想知道是否有某些情况下,这是不可能使用2)选项,但可以使用1)

+0

提示:如果将该概念用于垂直间距,则文字变得更容易阅读;所谓的段落?在标点符号之后保存空格字符的意义是什么?你看,你写的文本被阅读。所以你应该尽可能为我们做到这一点。 – GhostCat

+0

对不起,我很长一段时间没有使用服务 – nullbyte

+0

http://stackoverflow.com/a/34605591/1553851 – shmosel

回答

1

允许打破这种分解成部分:

<T extends Comparable<? super T>> 

这定义的T通用边界 - 它可以代表任何类型的即Comparable - 或者因为TComparable或因为某些父类是。由于该父级(称为P)可能实施Comparable<P>而不是Comparable<T>,因此有必要在此处使用super。听起来你已经知道了。

T 

这是该方法的返回类型,并且通常可以由呼叫上下文(当结果赋值给一个变量例如)来指定。我们会回到这个。

Collection<? extends T> 

这可以让你在TCollection A S或T任何亚型(称之为S)通过。如果您只指定Collection<T>,您只会只有能够通过Collection<T>对象而不是Collection<S>对象。

把它们放在一起这让呼叫者做这样的事情:

// notice that max() returns a C, but is assignable to a B 
B b = max(new ArrayList<C>()); 

ABC是:

A implements Comparable<A> 
B extends A 
C extends B 

事实证明,因为你的方法是直接返回一个TCollection<? extends T>并不重要,因为您始终可以将子类型(C)分配给父类型(B)。但是,如果我们调整方法的签名稍有恢复使用T作为通用型这不起作用:

public static <T extends Comparable<? super T>> Collection<T> 
    collect(Collection<T> collection) 

注意到,我们现在返回一个Collection<T> - 现在? extends变得非常重要 - 这并不编译:

Collection<B> b = collect(new ArrayList<C>()); 

而如果你将参数更改为Collection<? extends T>您放松的界限;返回类型不必与参数类型相同。你可以通过在Collection<C>但由于呼叫者使T意味着B(因为这是被预期作为返回类型的类型)C现在延伸T,而不是T

所以总之,对于你的方法我不相信你需要? extends T,但在某些情况下,你这样做。更一般地说,如果你打算在你的泛型中支持子类型,明确地这样做是一个好主意,即使它不是绝对必要的。

2

这适用于第一个版本,而不是第二:

class A implements Comparable<A> {...}; 
class B extends A {...}; 

List<B> list = new ArrayList<>(); 

B maxItem = max(list); 

在这两种情况下,类型参数T设置为B,因为listCollection<B>B不会实现Comparable<B>,但是,它不能满足第二个声明中的T extends Comparable<T>的限制。

它适用于第一个声明,但是,因为AB的超类。

这解释了Comparable<? super T> ...的Collection<? extends T>是不是真的有必要在这里 - 我不认为有任何理由的最大方法返回比集合元素类型T之外的类型,所以Collection<T>将被罚款。

+0

答案是承认,但用纯粹的话说:第二个实现坚持类型T为Comparable&Collection。第一个可以让你传递可比较和集合,它可以对T的子类型进行操作。 – sascha10000

+0

'B'不能满足这个约束,因为它实现了'可比较的'和'A'不扩展'B' –

1

要完全理解它,你缺少两个版本。这里有四个:

public static <T extends Comparable<? super T>> T max1(Collection<? extends T> collection){ 
    throw new UnsupportedOperationException("Not yet implemented"); 
} 
public static <T extends Comparable<T>> T max2(Collection<T> collection){ 
    throw new UnsupportedOperationException("Not yet implemented"); 
} 
public static <T extends Comparable<T>> T max3(Collection<? extends T> collection){ 
    throw new UnsupportedOperationException("Not yet implemented"); 
} 
public static <T extends Comparable<? super T>> T max4(Collection<T> collection){ 
    throw new UnsupportedOperationException("Not yet implemented"); 
} 

要看看他们是如何工作的,我会偷example by @MattTimmermans

class A implements Comparable<A> { 
    @Override public int compareTo(A that) { 
     throw new UnsupportedOperationException("Not yet implemented"); 
    } 
} 
class B extends A { 
} 

现在让我们来看看其中的四个最大方法的工作:

List<B> list = new ArrayList<>(); 

B maxItem1 = max1(list);   // T = B (but could be A if result is A, see next line) 
A maxItem1a = Test.<A>max1(list); // T = A 
B maxItem1b = Test.<B>max1(list); // T = B 

B maxItem2 = max2(list);   // compile error 

A maxItem3 = max3(list);   // T = A (forced by Comparable<T>) 

B maxItem4 = max4(list);   // T = B (forced by Collection<T>) 

正如您所看到的,由于缺少通配符,因此max3()max4(),T正被强制为特定类型。

max2()完全不起作用,因为没有T可以同时满足Comparable<T>Collection<T>

至于max1()T可被解析为任一AB,但如果它被解析为A,然后分配必须是A类型的变量。如果解析为B,则分配可以是AB
如果您没有手动指定T的类型,编译器将选择B


注意,因为你的最大方法将可能只是重复采集一次,你甚至可以更改参数类型为Iterable,一切仍能正常工作。

这将让你找到非集合iterables最大值,如java.nio.file.Path找到的最后一个文件(词典顺序)的文件夹中(如果是有道理的)