简体   繁体   中英

Generics Bounded Type Parameter

I have this design, and I'm not sure why it doesn't work.

interface BaseType {}

interface TypeA extends BaseType {}

interface TypeB extends BaseType {}

interface Query<T extends BaseType> {
    public String get();
}

interface Result<T extends BaseType> {
    public String get();
}

interface Service<T extends BaseType> {
    public Result<T> get(Query<T> query);
}

class SomeResult implements Result<TypeA> {
    private String s;
    public SomeResult(String s) { this.s = s; }
    public String get() { return this.s; }
}

class SomeQuery implements Query<TypeA> {
    public String get() { return "blah"; }
}

class SomeQuery2 implements Query<TypeA> {
    public String get() { return "blah2"; }
}

class SomeService implements Service<TypeA> {
    /** OK -- but notice the ambiguous parameter type */
    /*
    public SomeResult get(Query<TypeA> query) {
        if (query instanceof SomeQuery) return new SomeResult(query.get());
        else return null;
    }
    */

    /** NOT OK -- but this is the parameter I want to keep; notice SomeQuery IS-A Query<TypeA> */
    public SomeResult get(SomeQuery query) { return new SomeResult(query.get()); };
    /**
     * Main.java:27: error: SomeService is not abstract and does not override abstract method get(Query<TypeA>) in Service
     * class SomeService implements Service<TypeA> {
     * ^
     * 1 error
     */
}

public class Main {
    public static void main(String args[]) {
        SomeQuery someQuery = new SomeQuery();
        SomeQuery2 someQuery2 = new SomeQuery2();
        SomeService someService = new SomeService();
        System.out.println(someService.get(someQuery).get());
    }
}

I'm new to generics, and don't quite understand what contract I'm violating here. I want the service to be tightly bounded, and even if I can bound the return type, I cannot seem to do so for the parameter. Which means, I'll need to do an instanceof check inside the service to make sure I'm getting the right parameter. I want to avoid that. Any ideas?

You're allowed to make an overriding method's return type more specific due to return type covariance , but you can't change the method's parameters without changing its signature. That's why the compiler complains that you haven't implemented get(Query<TypeA>) when you change it to get(SomeQuery) . You'll need to make Service more flexible in order to get what you want:

interface Service<T extends BaseType, Q extends Query<T>> {
    public Result<T> get(Q query);
}

class SomeService implements Service<TypeA, SomeQuery> {

    @Override
    public SomeResult get(SomeQuery query) {
        ...
    }
}

Also note that the narrowed return type doesn't matter when coding to interface: when SomeService is typed as Service<TypeA, SomeQuery> , get will still return Result<TypeA> . So you might consider making a similar change for the result type:

interface Service<T extends BaseType, Q extends Query<T>, R extends Result<T>> {
    public R get(Q query);
}

class SomeService implements Service<TypeA, SomeQuery, SomeResult> {

    @Override
    public SomeResult get(SomeQuery query) {
        ...
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM