简而言之,我的问题是,是否,
interface Intf<T extends Bound> {}
class Clss<T extends Bound> {}
除了重复绑定类型之外,没有办法继承/实现它们:
class Impl<T extends Bound> implements Intf<T> {}
class Subc<T extends Bound> extends Clss<T> {}
我没有抱怨需要重复extends Bound
,我甚至理解编译逻辑,但我想知道这是否真的是这样。因为在两个地方使用<T>
在语义上看起来都足够了。
答案 0 :(得分:1)
是的,就是这样。这是每个人的充分解释。
这是你的例子的另一个版本(想象一下Integer可以扩展)
interface Intf<T extends Number> {}
class Clss<T extends Number> {}
class Impl<T extends Integer> implements Intf<T> {}
class Subc<T extends Integer> extends Clss<T> {}
class ImplSubc<T extends Integer> extends Clss<T> implements Intf<T> {}
<T extends Integer>
是初始化标记,其他<T>
标记基本上是“传递”引用。这两个标签在视觉上是相同的,但它们是不同的东西。请注意以下内容是非法的
class Subc<T extends Integer> extends Clss<T extends Integer> {}
因为传递标记需要SOMETHING ELSE来初始化它。所以你可以知道哪个是哪个,因为initialize标签最终会传递给它,而传递标签是根据初始化标签设置的内容传递(或返回)的东西。所以如果你有以下
class Subc<T> extends Clss<T> {}
编译单元说“允许将其设置为任何内容,然后将其传递给Clss”。由于您可以将初始化标记传递给多个传递标记,每个标记都有不同的边界,初始化标记NEEDS将被明确设置为确保传入的<T>
确实与您传递的所有内容兼容它一直到。虽然这在技术上可以推断,但是它昂贵,复杂,并且要求可怕的生产破坏错误,因此编译器只会迫使您知道自己在做什么。
更新:
下面是一个示例,说明如果让编译器决定您的代码是什么,错误如何进入生产。假设编译器确实推断了限制,If
class Clss<T extends Number> {}
class Subc<T> extends Clss<T> {}
成为
class Clss<T extends BigDecimal> {}
class Subc<T> extends Clss<T> {}
如果没有在{2}上设置extends Integer
并让它被推断,则此更改将进行编译(如果将其编译为与将其用作API的内容分开编译),并且可能仍然有效,直到一个案例没有。因为您说您明确希望使用Integer或Number,所以现在可以确定更改Clss的类型在编译时而不是运行时是安全的或坏的更改。 Java是一种安全第一类型语言,这意味着代码稳定性/可靠性检测是其功能的核心价值。所以当有疑问时,Java会抱怨。
答案 1 :(得分:1)
请注意,尽管Foo<T [constraints]> implements Bar<T>
模式很常见,但它只有一种可能性。以下是其他一些(非详尽的):
Foo<T extends DerivedBound> implements Bar<T>
Foo<T> implements Bar<DerivedBound>
Foo<T> implements Bar<ParameterisedBound<T>>
Foo<T, U extends Bound> implements Bar<U>
Foo<T extends BarBound & BazBound> implements Bar<T>, Baz<T>
因此,必须两次指定绑定没有冗余 - 您要向编译器提供两条独立的信息。
理论上,语言设计者可以在Java中添加一个特殊情况,以节省一些字符。但假设他们甚至考虑过这个选项,我想他们认为额外的复杂性并不是一个很好的权衡。