Java抽象类:为派生类返回“this”指针“跟进”

时间:2013-09-26 09:46:49

标签: java inheritance casting

我在下面的帖子上需要一些帮助,因为我无法使用泛型从基础对象中获取派生对象,我希望不使用向下转换。

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();

    }
}

2 个答案:

答案 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;
    }
}