Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Generics to Construct Instances of Child Classes

While looking at some Java 8 code I saw some use of generics that I didn't quite understand, so I wrote my own code to emulate what was going on:

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);
        }
    }
}

Running this prints:

Test_Child constructed with string 'Test'

What I don't understand is:

  1. Why don't you have to provide arguments to Test_Child::new
  2. How calling f.create() in the Product instance refers to the constructor of the Test_Child class.
like image 707
jipthechip Avatar asked Mar 28 '20 02:03

jipthechip


People also ask

Can generic class instance be created?

Yes, this is nice especially if the generic class is abstract, you can do this in the concrete subclasses :) @TimKuipers The <E> in class Foo<E> is not bound to any particular type.

Is it a good idea to use generics in collections?

By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.

Which construct is used to create generic classes or methods?

We use <T> to create a generic class, interface, and method. The T is replaced with the actual type when we use it.

Why do we use generics instead of object?

Generics could be used to develop a better solution using a container that can have a type assigned at instantiation, otherwise referred to as a generic type, allowing the creation of an object that can be used to store objects of the assigned type.


2 Answers

How you don't have to provide arguments to Test_Child::new

Since its a method reference for a representation of a lamda s -> new Test_Child(s) which is possible to create as the Factory interface ends up being a FunctionalInterface by its definition.

How calling f.create() in the Product instance refers to the constructor of the Test_Child class.

Since that's the instance type passed through the TestBuilder, to Product both having an attribute Factory<Test_Child>. It would be much clear when you rewrite the assignment as

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

To explain further as comments inlined with the code

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
like image 186
Naman Avatar answered Oct 16 '22 13:10

Naman


  1. The method awaits Factory<T> as the input parameter:

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

    And Factory is an interface with only one method:

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

    That makes it effectively a functional interface, that can be implemented by simply passing a lambda: Function<String, T> (a function that creates an instance of type T from String). Test_Child::new is such a lambda, because it consumes String and produces T.

  2. As stated Factory is a function, that takes a String and creates T. By calling the method create, we're invoking the function.

like image 20
Andronicus Avatar answered Oct 16 '22 12:10

Andronicus