Использование обобщений для построения экземпляров дочерних классов - PullRequest
6 голосов
/ 28 марта 2020

Просматривая некоторый код Java 8, я увидел использование обобщений, которые я не совсем понял, поэтому я написал свой собственный код для эмуляции происходящего:

public class GenericsTest {

    public static void main(String[] args) {
        TestBuilder tb = TestBuilder.create(Test_Child::new);
        Product<Test_Child> p = tb.build();

        Test tc = p.Construct("Test");
    }

    static class TestBuilder<T extends Test> {

        private final Factory<T> f;

        public TestBuilder(Factory<T> f) {
            this.f = f;
        }

        public static <T extends Test> TestBuilder<T> create(Factory<T> f){
            return new TestBuilder<>(f);
        }

        public Product<T> build(){
            return new Product<>(f);
        }

    }

    static class Test {
        public Test(){
        }
    }

    static class Test_Child extends Test{
        public Test_Child(String s){
            System.out.println("Test_Child constructed with string '"+s+"'");
        }
    }

    interface Factory<T extends Test> {
        T create(String s);
    }


    static class Product<T extends Test>{
        private Factory<T> f;

        public Product(Factory<T> f) {
            this.f = f;
        }

        public T Construct(String s){
            return f.create(s);
        }
    }
}

Выполнение этого печатает:

Test_Child constructed with string 'Test'

Я не понимаю:

  1. Почему бы вам не предоставить аргументы для Test_Child::new
  2. Как вызов f.create() в экземпляре Product относится к конструктору класса Test_Child.

Ответы [ 2 ]

4 голосов
/ 28 марта 2020

Как вам не нужно предоставлять аргументы для Test_Child :: new

Поскольку это ссылка на метод для представления лямды s -> new Test_Child(s), которую можно создать как Интерфейс Factory в конечном итоге становится FunctionalInterface.

Как вызов f.create () в экземпляре Product ссылается на конструктор класса Test_Child.

Поскольку это тип экземпляра, переданный через TestBuilder, Product оба имеют атрибут Factory<Test_Child>. Было бы очень ясно, когда вы переписываете назначение как

TestBuilder<Test_Child> tb = TestBuilder.create(Test_Child::new)

Чтобы объяснить дальше, как комментарии, вписанные с кодом

TestBuilder tb = TestBuilder.create(Test_Child::new); TestBuilder
// TestBuilder<Test_Child> is build with a Factory<Test_Child> attribute

Product<Test_Child> p = tb.build();
// We have build a Product<Test_Child> which has a Factory<Test_Child> attribute from above

Test tc = p.Construct("Test");
// invokes the 'create' method of the Factory which calls 'new Test_Child(s)' to print the output
3 голосов
/ 28 марта 2020
  1. Метод ожидает Factory<T> в качестве входного параметра:

    public static <T extends Test> TestBuilder<T> create(Factory<T> f)
    

    И Factory - это интерфейс только с одним методом:

    interface Factory<T extends Test> {
        T create(String s);
    }
    

    Это фактически делает его функциональным интерфейсом, который можно реализовать, просто передав лямбду: Function<String, T> (функция, которая создает экземпляр типа T из String). Test_Child::new такая лямбда, потому что она потребляет String и производит T.

  2. Как указано, Factory - это функция, которая принимает String и создает T. Вызывая метод create, мы вызываем функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...