【问题标题】:self-referential methods with generic return type for multiple inherited classes具有多个继承类的通用返回类型的自引用方法
【发布时间】:2012-06-21 17:20:41
【问题描述】:

这可能有点难以描述。不过,我会试试看;)

遵循流畅的风格,一个类的方法通常返回类实例本身(this)。

public class A {

    public A doSomething() {

        // do something here

        return this;
    }

}

当扩展这样一个流畅的样式类时,在第一个继承步骤中通过泛型类型并在超类中将返回类型转换为该泛型类型,可以很容易地做到这一点。

public class A<T extends A<T>> {

    public T doSomething() {

        // do something here

        return (T) this;
    }

}

public class B extends A<B> {

    // an extended class of class A

}

但是,当在这个扩展类上执行另一个继承步骤时,我在尝试定义方法的通用返回类型(在上层类中)和类描述本身时遇到了麻烦,例如,在super-super 类不会返回扩展类的类型,而是返回超类的类型。然而,我的意图是这些流畅风格的方法应该总是返回当前类的类型(而不是上层类)。

那么是否可以通过使用泛型来定义解决方案?

PS:我知道,一个简单的解决方法可能是覆盖扩展类中的所有这些方法并将它们转换为当前类型。但是,我对更优雅的解决方案感兴趣;)

【问题讨论】:

标签: java generics inheritance self-reference


【解决方案1】:

你可以试试:

class A<T> { }

class B<T extends A<? super T>> extends A<T> { }

class C extends B<C> { }

【讨论】:

    【解决方案2】:

    你可以这样做:

    public class TestFluent<T extends TestFluent<?>> {
    
        public T get() {
            return (T) this;
        }
    
        public static void main(final String[] args) {
            TestFluent2<TestFluent2<?>> f2 = new TestFluent2<TestFluent2<?>>();
            TestFluent2<?> result2 = f2.get();
    
            TestFluent3<TestFluent3<?>> t3 = new TestFluent3<TestFluent3<?>>();
            TestFluent3<?> result3 = t3.get();
    
            System.out.println(result2);
            System.out.println(result3);
        }
    }
    
    class TestFluent2<T extends TestFluent2<?>> extends TestFluent<T> {
    }
    
    class TestFluent3<T extends TestFluent3<?>> extends TestFluent2<T> {
    }
    

    这将返回:

    TestFluent2@7919298d
    TestFluent3@62f72617
    

    【讨论】:

    • 是的,这看起来很有希望。之前也尝试过使用通配符,但目前还没有找到适用的解决方案。
    【解决方案3】:

    一般不能“返回当前类的类型”。

    即使您在上面发布的代码也是不安全的:public class C extends A&lt;B&gt; 是合法的,但是在上面调用 doSomething(),期待 B,将会崩溃。

    【讨论】:

    • ... 因为它是 public class B extends A 而不是 public class C extends A ;)
    • 我是说在你定义public class B extends A&lt;B&gt;之后再定义public class C extends A&lt;B&gt;是合法的,不是吗?
    • ...当然,这是合法的。但是,它没有导致预期的行为,或者? (......无论如何......)
    • 然后当你做C foo = ...; B bar = foo.doSomething();它会崩溃。所以不安全
    • 再说一次,这不是我想做的:我想做:B foo = ...; foo.doSomething().doSomethingElse().doSomethingDifferent()。 ...所以它总是返回 B(自引用)。因此,请不要将所描述的用例与其他内容混为一谈。
    【解决方案4】:

    使用委托来实现可扩展的流畅接口。优点:没有演员表。缺点:冗长。

    public interface FluentGranddad< C extends FluentGranddad< C > > {
        C appendFoo( Foo foo );
    }
    public interface FluentDad< C extends FluentDad< C > > extends FluentGranddad< C > {
        C appendBar( Bar b );
    }
    public interface FluentKid< C extends FluentKid< C > > extends FluentDad< C > {
        C appendSplat( Splat s );
    }
    
    public class Babbler implements FluentKid< Babbler > {
        FluentDad< ? > dad;
        public Babbler( FluentDad< ? > dad ) {
            this.dad = dad;
        }
    
        // delegation methods
        @Override public Babbler appendFoo( Foo foo ) {
            dad.appendFoo( foo );
            return this;
        }
        @Override public Babbler appendBar( Bar bar ) {
            dad.appendBar( bar );
            return this;
        }
    
        // example instance method
        @Override public Babbler appendSplat( Splat s ) {
            dad.getState().append( s.toString() );
            return this;
        }
    }
    

    【讨论】:

    • 是的,我知道这是我目前应用的简单解决方法。但是,在引入新的扩展层时,总是重复这一点有点麻烦(从我的 POV 来看)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多