Bober02
Bober02

Reputation: 15341

Static generic interface implementation

I have a simple interface:

interface Predicate<T> {

boolean accepts(T in);

}

Now I would also like to provide sth of the form:

interface Predicate<T> {

public static Predicate<T> ALL = new Predicate<T>(){ ... }

boolean accepts(T in);

}

This is not legal in the form presented above. Is there a way to provide a generic implementation that would be typed i.e. in multiple context I could say Predicate.ALL, a bit like Collections.EMPTY_SET?

UPDATE: I meant interface...

Upvotes: 0

Views: 164

Answers (5)

Seelenvirtuose
Seelenvirtuose

Reputation: 20618

There are two ways to implement the ALL predicate:

public interface Predicate<T> {
    public static final Predicate<Object> ALL = new Predicate<Object> {
        @Override public boolean accepts(Object in) { return true; }
    }
    boolean accepts(T in);
}

You are declaring a concrete class field (a constant) here, so you must use a concrete replacement for the type variable T. As you are not interested in the type, you use the supertype of all objects: java.lang.Object.

This implementation would satisfy the compiler to not generate any warning and is a good starting point. However, you have some difficulties. Consider this code:

public class PredicateTester {
    public static void main(String[] args) {
        test1(Predicate.ALL, "some string"); // compiler error
        test2(Predicate.ALL, "some string");
    }
    public static void test1(Predicate<String> pred, String in) {
        System.out.println(pred.accepts(in) ? "pass" : "fail");
    }
    public static void test2(Predicate<? super String> pred, String in) {
        System.out.println(pred.accepts(in) ? "pass" : "fail");
    }
}

Although test1 and test2 are both valid methods (and compile fine), the call to method test1 will not compile. Simply put: A Predicate<Object> is not a Predicate<String>.

Conclusion: You must remember the PECS (producer extends, consumer super) when designing your methods that will take a Predicate as an argument.

The other option is to just not provide a type at all:

public interface Predicate<T> {
    @SuppressWarnings("rawtypes")
    public static final Predicate ALL = new Predicate {
        @Override public boolean accepts(Object in) { return true; }
    }
    boolean accepts(T in);
}

With that implementation the above mentioned class PredicateTester compiles just fine. So, this is the way to go.

Upvotes: 1

Phil Parker
Phil Parker

Reputation: 1647

In the form that you've written it you need it to be abstract as you're not providing the implementation of the accepts method.

abstract class Predicate<T> {

    abstract boolean accepts(T in);

}

If you then want to provide a multi use "accept any" predicate you can do it like this:

public static Predicate ALL = new Predicate(){
    @Override
    boolean accepts(Object in) {
        return true;
    }
};

@SuppressWarnings("unchecked")
static final <T> Predicate<T> all(){ return (Predicate<T>)ALL;}

This mimics the way that Collections.EMPTY_SET and Collections.emptySet() work.

Note that Collections.EMPTY_SET (and Predicate.ALL) are not type-safe but Collections.emptySet() (and Predicate.all()) will infer the type they are being assigned to.

Upvotes: 0

Sebastian Redl
Sebastian Redl

Reputation: 71939

To start with, your simple class is invalid because accepts doesn't have an implementation nor is it abstract. Let's assume you meant to make it abstract, so that actual predicates are derived classes that override it. Then the ALL predicate is one that always returns true. But you can't make it a static field, because static fields cannot reference the type parameter.

public abstract class Predicate<T> {
  public abstract boolean accepts(T in);

  public static <T> Predicate<T> all() {
    return new Predicate<T> { boolean accepts(T in) { return true; } };
  }
}

Upvotes: 1

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13525

you only need to replace <T> with something meaningful in the declaration of ALL. Or remove altogether - note, Collections.EMPTY_SET is of type Set, not Set<T>.

Upvotes: 1

Thomas
Thomas

Reputation: 88707

You could use type inference just like Collections.emptySet() (note that Collections.EMPTY_SET doesn't use generics).

From Collections:

public static final Set EMPTY_SET = new EmptySet();

public static final <T> Set<T> emptySet() {
  return (Set<T>) EMPTY_SET;
}

You could mimic it like this:

public static Predicate ALL = new Predicate(){ ... }   

public static final <T> Predicate<T> all() {
  return (Predicate<T>) ALL;
}

Upvotes: 3

Related Questions