2014-11-25 94 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; 
    } 

}