我知道这不是一个很好的问题,我可能会受到诅咒但是我找不到任何地方可以帮助解决这个问题
下面是我的面试问题中出现的一个通用课程(我已经失败了)。问题是告诉这个类声明正在做什么以及在什么情况下可以使用它?
我对通用编程的理解非常有限,但我理解'T'是Type,'extends'在这里意味着Type应该继承'SimpleGenericClass',但我不理解'?'最后以及在什么情况下该类可能被用于
public abstract class SimpleGenericClass<T extends SimpleGenericClass<?>> {
}
答案 0 :(得分:4)
首先,因为类SimpleGenericClass
是 abstract ,所以它应该是子类。
其次,它是一个泛型类,这意味着在类的某个地方,你几乎肯定会使用泛型参数T
作为字段的类型。
public abstract class SimpleGenericClass<T...> {
T x;
}
现在第一个有趣的事情是T
有界。因为它被声明为T extends SimpleGenericClass<?>
,所以它只能是SimpleGenericClass<?>
或SimpleGenericClass<?>
的某个子类。您还询问了thr ?
。这被称为通配符,在the Java Tutorial on Wildcards有一个非常好的解释。在你的情况下,我们会说这是一个“未知的SimpleGenericClass”。在Java中需要它,因为SimpleGenericClass<Object>
不是SimpleGenericClass<String>
的超类,例如。
第二个有趣的是,由于T
是某种类型的SimpleGenericClass
,因此您的类很可能定义递归结构 。我想到的是树(想到表达式树),其中SimpleGenericClass
是(抽象)节点类型,设计为使用各种专用节点类型进行子类化。
更新 This SO question on self-bounded generics可能会对您有所帮助。
更新2
我继续编写了一些代码,说明了如何使用它。该应用程序不执行任何操作,但它会进行编译,它会向您显示通用边界如何提供一些可能有意义的约束。
public abstract class Node<T extends Node<?>> {
public abstract T[] getChildren();
}
class NumberNode extends Node {
int data;
public Node[] getChildren() {return new Node[]{};}
}
class IdentifierNode extends Node {
int data;
public Node[] getChildren() {return new Node[]{};}
}
class PlusNode extends Node {
NumberNode left;
NumberNode right;
public NumberNode[] getChildren() {return new NumberNode[]{};}
}
这里的好处是NumberNode[]
是PlusNode.getChildren
的有效返回类型!在实践中这有关系吗?不知道,但很酷。 :)
这不是最好的例子,但问题是相当开放的(“这样的东西可能用于什么?”)。当然,还有其他方法可以定义树。
答案 1 :(得分:0)
这实际上只意味着您允许类SimpleGenericClass的用户参数化类型为T的类的实例。但是,T不能是任何类型,但必须是SampleGenericClass(或SampleGenericClass本身)的子类型。
在SimpleGenericClass类的其余代码中,您可以在方法签名中使用类型T.
让我们假设SimpleGenericClass不是抽象的。使用它时,您可以写:
new SimpleGenericClass<SampleGenericClass<String>>();
即。使用SampleGenericClass和带有String的SampleGenericClass对SimpleGenericClass进行参数化。
答案 2 :(得分:0)
根据定义,它表示SimpleGenericClass
可以处理类型<T>
,它是SimpleGenericClass的子类。
所以我假设会有一些可以在<T>
上运行的操作。
现在看看为什么要定义这样的模板 - (我真的不能想到),可能是SimpleGenericClass
是一个抽象类的场景(刚才意识到它是按照OP:P )并期望它可以适用于任何具体的类?
伙计们你觉得怎么样?
答案 3 :(得分:0)
这基本上是sais:在这个类中你有一个名为T的Type占位符,以及对该占位符的限制,它必须是SimpleGenericClass类型或扩展它的东西。一旦遵守该规则,您就可以创建类的实例并为T提供实际类型,稍后可以在该类的方法中使用,如下所示:
public class C <T extends Number>{
public void doSomething(T t) {
}
public static void main(String... args) {
//works:
C<Number> c = new C<Number>();
c.doSomething(new Number() {
//Aonimous implementation of number
});
//won't work
//C<Object> c = new C<Object>();
C<Integer> c2 = new C<Integer>();
c2.doSomething(new Integer(1));
//won't work
//c2.doSomething(new Number() {
//Aonimous implementation of number
//});
}
}
此时SimpleGenericClass<?>
非常多余。如果此类需要其他泛型类型,则可以有多个(SimpleGenericClass<T extends SimpleGenericClass, T2 extends Whatever>
)
答案 4 :(得分:0)
我猜你已经以这种形式提出了问题(T
而不是?
):
public abstract class SimpleGenericClass<T extends SimpleGenericClass<T>>
看看这段代码:
abstract class Foo<SubClassOfFoo extends Foo<SubClassOfFoo>>
{
/** subclasses are forced to return themselves from this method */
public abstract SubClassOfFoo subclassAwareDeepCopy();
}
class Bar extends Foo<Bar> {
public Bar subclassAwareDeepCopy() {
Bar b = new Bar();
// ...
return b;
}
}
Bar b = new Bar();
Foo<Bar> f = b;
Bar b2 = b.subclassAwareDeepCopy();
Bar b3 = f.subclassAwareDeepCopy(); // no need to cast, return type is Bar
Foo<SubClassOfFoo extends Foo<SubClassOfFoo>>
的诀窍是:
Foo
的任何子类都必须为Foo
提供类型参数。 Foo
的子类。 Foo
的子类(如Bar
)遵循该类型的习语
他们提供给Foo
的论据就是他们自己。
Foo有一个返回SubClassOfFoo
的方法。综合
用上面的成语,这允许Foo制定合同
说“我的任何子类都必须实现subclassAwareDeepCopy()
和
他们必须声明它返回实际的子类“。
换句话说:这个习惯用法允许超类(例如抽象工厂)定义其参数类型和返回类型是根据子类类型而不是超类类型的方法。
诀窍是在Enum
JDK类:
public abstract class Enum<E extends Enum<E>>
有关详细信息,请参阅here。