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:
Test_Child::new
f.create()
in the Product
instance refers to the
constructor of the Test_Child
class.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.
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.
We use <T> to create a generic class, interface, and method. The T is replaced with the actual type when we use it.
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.
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
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
.
As stated Factory
is a function, that takes a String
and creates T
. By calling the method create
, we're invoking the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With