koikahin
koikahin

Reputation: 23

Can a class's generic type parameters be inferred from the arguments of its constructor?

Here is what I want to do:

I have an interface Service as follows:

public interface Service<A extends Object, R extends Object> {
    R run(A a);
}

Now, I want to link two such services, while keeping it generic:

public class Link<A, R> implements Service<A, R> {

    private Service<A, X> s1; // fail
    private Service<X, R> s2; // fail

    public <X> Link(Service<A, X> s1, Service<X, R> s2) {
        this.s1 = s1;
        this.s2 = s2;
    }

    @Override
    public R run(A a) {
        return s2.run(s1.run(a));
    }
}

The problem that I want to address is to assign a generic type X to class Link, but I want that to be inferred through its constructor and not through the class declaration.

How is it achievable?

Edit:

The way I've solved this is by a utility static method which does the linking process:

public static <A, I, R> Service<A, R> link(final Service<A, I> s1, final Service<I, R> s2) {
    return new Service<A, R>() {
        @Override
        public R run(A a) throws Exception {
            I intermediate = s1.run(a);
            return s2.run(intermediate);
        }
    };
}

Upvotes: 2

Views: 106

Answers (3)

Louis Wasserman
Louis Wasserman

Reputation: 198211

You cannot have a generic type X that is part of your class (not selected anew for each method call) that is not part of its declaration.

Sure, you could hide the unsafe casts and be certain your program would work, but those are really your only two options in the limitations of Java's type system.

What I would do, though, is instead of providing a Link constructor, provide the static factory method

public static <A, X, R> Service<A, R> link(Service<A, X> s1, Service<X, R> s2) {
  return new Link<A, X, R>(s1, s2);
}

...ensuring that X is automatically inferred and never escapes.

Upvotes: 4

Tom McIntyre
Tom McIntyre

Reputation: 3699

For better covariance you could adjust the above answers like this:

public class Link <F, X, T>  implements Service <F, T> {
    private final Service<F, ? extends X> from;
    private final Service <X, ? extends T> to;

    public Link(Service <F, ? extends X> from, Service <X, ? extends T> to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public T run (F input) {
        return to.run (from.run (input));
    }
}

Upvotes: 1

Amir Pashazadeh
Amir Pashazadeh

Reputation: 7322

This will work:

public class Link<A, R, X> implements Service<A, R> {

    private Service<A, X> s1; 
    private Service<X, R> s2; 

    public Link(Service<A, X> s1, Service<X, R> s2) {
        this.s1 = s1;
        this.s2 = s2;
    }

    @Override
    public R run(A a) {
       return s2.run(s1.run(a));
    }
}

Upvotes: 0

Related Questions