Skip to content

Instantly share code, notes, and snippets.

@lmr3796
Last active May 16, 2020 08:56
Show Gist options
  • Save lmr3796/2717c31e75bb3233d931678b210c311b to your computer and use it in GitHub Desktop.
Save lmr3796/2717c31e75bb3233d931678b210c311b to your computer and use it in GitHub Desktop.

我重新組織了一個範例。先假設沒有什麼權限或dependency的問題。先說我是用Java 8,我不知道後面版本的泛型推導有沒有變得比較智能:""(((

class Framework {
    public static <T, F extends Factory<T>> T resolve(Class<F> clazz) {
        try {
            return clazz.newInstance().getInstance();
        } catch (Exception e) {
            return null;
        }
    }
}

abstract class Factory<T> {
    abstract public T getInstance();
}

然後有一些我自己定義的interface和對應的實作與factory們

interface MyBean {}
class BeanX implements MyBean {}
class BeanY implements MyBean {}

class BeanXFactory extends Factory<BeanX> {
    @Override
    public BeanX getInstance() {
        return new BeanX();
    }
}
class BeanYFactory extends Factory<BeanY> {
    @Override
    public BeanY getInstance() {
        return new BeanY();
    }
}

接著呢,我想要幹這麼一件事,從一個list of factory class,一個一個resolve出他們對應的bean

public class GenericTest {
    public static void main(String[] args) {
        // 現時情況可能是想從一個file read class name然後reflection,但為了解說方便我就定一個list
        final List<Class<? extends Factory<? extends MyBean>>> factoryClasses =
                Arrays.asList(
                    BeanXFactory.class,
                    BeanYFactory.class
                );
        List<MyBean> beanList = new ArrayList<>();
        factoryClasses.forEach(fc -> {
            beanList.add(Framework.resolve(fc));
        });
    }
}

這個想法是,list那邊我下了一個Factory<? extends MyBean>來表達我Factory create的instance一定是MyBean的實作 所以我Framework.resolve的時候每個出來的物件都會是MyBean,我就可以好好地收集起來惹

但是這個compile不會過:)))

在Java傻逼泛型下,沒有下外卡的話Generic<Children>是不能成為foo(Generic<Parent> input)的參數的, 泛型參數在沒有下wildcard的情況是沒做多型的

我這個的案例關鍵在Framework那裡他吃的是一個<T, F extends Factory<T>>,這個參數F必須要是一個Factory,T要是可以直接推出來的型別 但我的list裡面的factory class們他們是Factory<? extends MyBean>,他是有bound的,所以參數要的type equality就對不起來了...

要改到過的話有兩招

  1. Framework也改成吃有wildcard的

    /*before*/ public static <T, F extends Factory<T>> T resolve(Class<F> clazz)
    
    /*after*/  public static <T, F extends Factory<? extends T>> T resolve(Class<F> clazz)
  2. 不要寫迴圈,手動展開讓他一個一個resolve...

    public static void main(String[] args) {
        List<MyBean> beanList = new ArrayList<>();
        beanList.add(Framework.resolve(BeanXFactory.class));
        beanList.add(Framework.resolve(BeanYFactory.class));
    }

我是覺得這很沒道理啦,我覺得Framework應該要可以推得出來T的boundary下界就是我參數的上界MyBean啊 :(
有智慧的J也是這麼覺得沒噴錯,但看來事情沒這麼簡單:(((

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment