我在下面的帖子上需要一些帮助,因为我无法使用泛型从基础对象中获取派生对象,我希望不使用向下转换。
Java Abstract Classes: Returning "this" pointer for derived classes
我非常有兴趣看到@Adam Arold在帖子中设置的代码示例。
public <T extends KeyException> T withId(final String Id) {
this.Id = Id;
return (T)this;
}
我无法对该帖子发表评论,因为我需要代表这样做,所以我发布了新帖。
我基本上尝试了这个并且收到一些警告,
public class KeyException <T extends KeyException<T>> {
private static final long serialVersionUID = 1L;
protected String Id;
public String getId() {
return Id;
}
// The type parameter T is hiding the type T
public <T extends KeyException<T>> T withId(final String Id) {
this.Id = Id;
return (T)this; //Type safety: Unchecked cast from KeyException<T> to T
}
}
使用泛型来避免向下转换的正确方法是什么?
我的代码如下,
class Foo<TSelf extends Foo<?>> {
Class<TSelf> selfClass;
Foo(Class<TSelf> selfClass) {
if (!this.getClass().equals(selfClass))
throw new IllegalArgumentException();
this.selfClass = selfClass;
}
TSelf returnThis() {
return selfClass.cast(this);
}
}
class Bar extends Foo<Bar> {
Bar() {
super(Bar.class);
}
public int id = 10;
public static void main(String str[]) {
Bar b = new Bar();
b.id = 10;
Foo F = new Bar();
F.returnThis().id = 20; // Error, id cannot be resolved or is not a field
}
}
使用“ Fluent ”界面实施构建器模式,
// Base Class here
public abstract class Foo {
protected final String className;
protected Foo(Builder<?> builder) {
this.className = builder.className;
}
/* Builder class */
public static abstract class Builder<T extends Builder<?>> {
Class<T> selfClass;
protected String className;
public Builder(Class<T> selfClass) {
if (!this.getClass().equals(selfClass))
throw new IllegalArgumentException();
this.selfClass = selfClass;
}
public Builder<?> withClassName(String className) {
this.className = className;
return self();
}
public T self() {
return selfClass.cast(this);
}
/* build the Option */
public abstract Foo build();
private void validate() {
if(className != null)
throw new IllegalArgumentException();
}
}
}
// Derived Class here
public class Bar extends Foo {
private final String barProp;
private Bar(Builder builder) {
super(builder);
this.barProp = builder.barProp;
}
/* Builder class */
public static class Builder extends Foo.Builder<Bar.Builder> {
private String barProp = null;
public Builder() {
super(Bar.Builder.class);
}
public Bar.Builder withBarProp(String barProp) {
this.barProp = barProp;
return self();
}
public Bar build() {
if(barProp != null)
throw new IllegalArgumentException();
return new Bar(this);
}
}
public static void main(String[] args) {
Foo f1 = new Bar.Builder()
.withBarProp("Prop 1")
.withClassName("Bar") // works fine
.build();
Foo f2 = new Bar.Builder()
.withClassName("Bar")
.withBarProp("Prop 1") // does not work
.build();
}
}
答案 0 :(得分:2)
您正在隐藏T
中的withId()
参数。这意味着方法中使用的T
与类范围中使用的T
不同。只需使用:
public T withId(String id) {
...
}
那就是说,你想要做的事情可能无法成为类型安全的。 KeyException
无法保证this
的类型为T
。类型系统无法表达这种约束,我有一种预感,甚至不能这样做,因为它会造成比它解决的更多麻烦。
您可以获得的最接近的仍然是样板式解决方法,仅适用于一个级别的继承:
class Foo<TSelf extends Foo> {
Class<TSelf> selfClass;
Foo(Class<TSelf> selfClass) {
if (!this.getClass().equals(selfClass))
throw new IllegalArgumentException();
this.selfClass = selfClass;
}
TSelf returnThis() {
return selfClass.cast(this);
}
}
class Bar extends Foo<Baz> {
Bar() {
super(Baz.class);
}
}
这仍然需要您拼出您希望withId()
等方法返回的类型,但至少您不必覆盖它们中的每一个。
答案 1 :(得分:1)
为避免向下转换,您可以将需要返回自我类型的withId
方法拉入接口。然后,抽象类可以实现具有自身类型的接口。然后,它允许它避免垂头丧气。抽象类的任何子类都可以使用返回类型协方差安全地返回它。
这可以避免向下转换,但这意味着抽象类的子类没有使用自我类型键入,这可能是一个问题,具体取决于您的用例。
public interface IFace<SELF extends IFace<SELF>> {
SELF withId(String id);
}
abstract class Type1 implements IFace<Type1> {
@Override
public Type1 withId(String id) {
return this;
}
}
class Type2 extends Type1 {
@Override
public Type2 withId(String id) {
return this;
}
}