Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infer generic class type parameters from single constructor parameter

What I want is following:

Most of the time, the generic class will be like TestBuilder<X, X>, meaning that T and O are of the same type. Therefore I create two different constructor. I want to make anonoumous new calls like new TestBuilder<>(...) (I'm calling the <> anonoumous here).

Following 4 constructor example exists:

1) Working constructor calls

// Anonoumous, working
new TestBuilder<>(String.class, Integer.class)
    .withOnNext(new Action1<Integer>()
    {
        @Override
        public void call(Integer integer)
        {   
        }
});

// not anonoumous, classified, working
new TestBuilder<String, String>(String.class)
    .withOnNext(new Action1<String>()
    {
        @Override
        public void call(String string)
        {
        }
});

2) Constructor Calls with problems or not working

// Anonoumous and working
// PROBLEM: withOnNext is called with Object instead of String
new TestBuilder<>(String.class)
    .withOnNext(new Action1<Object>()
    {
        @Override
        public void call(Object o)
        {
        }
});

// Anonoumous and NOT working
// this is what I want to work!
new TestBuilder<>(String.class)
    .withOnNext(new Action1<String>()
    {
        @Override
        public void call(String string)
        {
        }
});

Question

Is there a way to get the 4th constructor to work? I don't want to be forced to give the constuctor two classes if I call it with one argument only, the second generic class should "inherit" from the first in this case... Instead of having to write new TestBuilder<String, String>(String.class) I want to write new TestBuilder<>(String.class) or at least new TestBuilder<String>(String.class)...

Class

This is what the test builder class looks like:

public class TestBuilder<T, O>
{
    public TestBuilder(Class<T> eventClass)
    {
        this(eventClass, (Class<O>)eventClass);
    }

    private TestBuilder(Class<T> eventClass, Class<O> observableClass)
    {
        init();
    }

    public TestBuilder<T, O> withOnNext(Action1<O> actionNext)
    {
        mActionNext = actionNext;
        return this;
    }
}
like image 741
prom85 Avatar asked Oct 31 '22 03:10

prom85


1 Answers

I don't think Java can infer the second generic type without some kind of hint. One way is giving the type in variable declaration:

 TestBuilder<String, String> testBuilder = new TestBuilder<>(String.class);
 testBuilder.withOnNext(new Action1<String>() {
     @Override
     public void call(String string) {
         //...
     }
 });

But you'd still need to declare both generic parameters.

What I would do is encapsulating the information that both T and O are the same in a static factory method:

public  class TestBuilder<T, O> {
    public static <T> TestBuilder<T, T>  create(Class<T> eventClass) {
        return new TestBuilder<T, T>(eventClass);
    }
    // ...
}

and then call it like this:

TestBuilder.create(String.class).withOnNext(...);

Yet another option is encapsulating the information in a separate class inheriting from TestBuilder:

public class SimpleTestBuilder<T> extends TestBuilder<T,T> {
    public SimpleTestBuilder(Class<T> eventClass) {
        super(eventClass, eventClass);
    }
}

public class TestBuilder<T, O> {
    private TestBuilder(Class<T> eventClass, Class<O> observableClass) {
    }
    // ...
}

Used as

    new SimpleTestBuilder<>(String.class).withOnNext(...);

Yet another good option is to encapsulate the information O is same as T in a static method:

public  class TestBuilder<T, O> {
    public static <T> TestBuilder<T, T>  create(Class<T> eventClass) {
        return new TestBuilder<T, T>(eventClass);
    }
    // ...
}

Used as

TestBuilder.create(String.class).withOnNext(...);
like image 111
Mifeet Avatar answered Nov 04 '22 07:11

Mifeet