2014-11-25 102 views
4

我需要提供一个通用接口用于单独的类层次结构,并希望该接口支持调用链接。反射泛型类型边界和方法链接

我试图做到这一点使用反射边界,但我似乎无法得到它的工作,而不必将“this”投射到所需的类型。这是我目前的做法:

public interface Operable<T extends Operable<T>> { 
    T prepare(); 
    T operate(); 
} 

public abstract class BaseOperable<T extends Operable<T>> implements Operable<T> { 
    @Override 
    public T prepare() { 
    System.out.println("Preparing..."); 
    // why is this needed? "this" is a BaseOperable that implements Operable<T> 
    return (T) this; 
    } 
} 

public class SpecialOperable<T extends SpecialOperable<T>> extends 
    BaseOperable<SpecialOperable<T>> { 
    @Override 
    public T operate() { 
    System.out.println("Operation " 
      + (Math.random() > 0.5 ? "succeeded" : "failed")); 
    // this seems to be required 
    return (T) this; 
    } 

    @Override 
    public T prepare() { 
    // if I don't override this, the supertype T is used which is 
    // BaseOperable and hides "specialOp" from chaining 
    return (T) super.prepare(); 
    } 

    public T specialOp() { 
    System.out.println("Doing something special..."); 
    return (T) this; 
    } 
} 

以上下面一行的代码编译: mySpecialOperable().prepare().operate().specialOp().operate();

我的问题是:有没有办法避免每return语句的类型转换?是否有可能不需要覆盖最专业级别的所有内容(如prepare()方法所做的那样)?

回答

0

的问题是,你在哪里编译器不能在编译时确定的地方假设类型安全如果一个演员合法。这需要你遇到的警告:为了使这个更清晰,假设:

class Foo implements Operable<Foo> { ... } // legal 
class Bar implements Operable<Foo> { ... } // not intended, but also legal 

Bar类不打算以你的抽象模型是合法的。你想实现一个自我类型,但是你真正需要的所有东西都需要扩展一个类型为T的实现给定接口。因此,延长

class Bar extends BaseOperable<Foo> { ... } 

基本上会在运行时执行的,如果你实现:

class Bar implements Operable<Foo> { 
    @Override 
    public Foo prepare() { 
    System.out.println("Preparing..."); 
    // Why is this needed, you ask? Because you are now expressing this: 
    return (Foo) this; // this is of type Bar, not Foo 
    } 
    ... 
} 

然而,当thisBar实例,但不是Foo。这当然会导致ClassCastException,并且为了这种可能性,编译器会警告您静态类型安全性已经失败,并且可能会在您通常不期望的情况下发生此异常。

您通常通过添加方法等避免该:

protected abstract T self(); 

然后由任何非抽象类来实现。由于此方法随后使用非泛型返回类型实现,因此静态编译检查可以完成其工作,并禁止您使用任何非法返回类型,如前所述。那么上述prepare方法将实施这样的:

public T prepare() { 
    System.out.println("Preparing..."); 
    return self(); 
} 

但是,如果将此与不被任何用户实施了封闭的API工作,要采取走捷径(希望,你有单元测试验证是否存在任何可能的滥用),您可以使用@SupressWarnings("unchecked")注释方法来告诉编译器,您知道您正在处理的内容。

0

你的问题可以简化为:

public abstract class AbstractOperable<T extends AbstractOperable<T>> { 

    public T prepare() { 
     // Type mismatch: cannot convert from AbstractOperable<T> to T 
     return this; 
    } 

} 

public class OperableImpl extends AbstractOperable<OperableImpl> { 

} 

现在,考虑下面的类:

public class OperableImplHack extends AbstractOperable<OperableImpl> { 

} 

虽然它满足了T extends AbstractOperable<T>合同,thisT。这就是为什么编译器不知道this是否为T或不在AbstractOperable中的原因。


解决方案#1:

public abstract class AbstractOperable<T extends AbstractOperable<T>> { 

    public abstract T getThis(); 

    public T prepare() { 
     return getThis(); 
    } 

} 

public class OperableImpl extends AbstractOperable<OperableImpl> { 

    @Override 
    public OperableImpl getThis() { 
     return this; 
    } 

} 

解决方案2:

public abstract class AbstractOperable<T extends AbstractOperable<T>> { 

    protected T that; 

    public T prepare() { 
     return that; 
    } 

} 

public class OperableImpl extends AbstractOperable<OperableImpl> { 

    public OperableImpl() { 
     that = this; 
    } 

}