1

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

Java 抽象类:返回派生类的“this”指针

我对看到@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();

    }
}
4

2 回答 2

2

您正在将T参数隐藏在withId(). 这意味着方法中使用的与类范围中使用的T不同。T只需使用:

public T withId(String id) {
    ...
}

也就是说,您尝试执行的操作可能无法实现类型安全。KeyException根本无法保证的类型thisT。类型系统无法表达这种约束,我有一种预感,它甚至不能这样做,因为它会造成比它解决的更多的麻烦。

您可以得到的最接近的是仍然样板的解决方法,它仅适用于一级继承:

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()etc... 方法返回什么类型,但至少您不必重写它们中的每一个。

于 2013-09-26T09:50:27.870 回答
1

为了避免向下转换,您可以将withId需要返回自身类型的方法拉到接口中。然后,抽象类可以使用自身的自身类型来实现接口。然后,它可以避免沮丧。抽象类的任何子类都可以使用返回类型协方差安全地返回 this。

这避免了向下转换,但这意味着抽象类的子类没有使用自类型进行类型化,这可能是一个问题,具体取决于您的用例。

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;
    }
}
于 2013-09-26T10:30:58.413 回答