Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type safety, Java generics and querying

Tags:

java

generics

I have an interesting situation and I'm wondering if there is a better way to do this. The situation is this, I have a tree structure (an abstract syntax tree, specifically) and some nodes can contain child nodes of various types but all extended from a given base class.

I want to frequently do queries on this tree and I'd like to get back the specific subtypes I'm interested in. So I created a predicate class that I can then pass into a generic query method. At first I had a query method that looked like this:

public <T extends Element> List<T> findAll(IElementPredicate pred, Class<T> c);

where the Class argument was used just to indicate the return type. What bothered me about this approach is that all of my predicates were already for specific types so there is redundant information here. A typical call might look like:

List<Declaration> decls = 
    scope.findAll(new DeclarationPredicate(), Declaration.class);

So I refactored it like this:

public <T extends Element> List<T> findAll(IElementPredicate<T> pred);

Where the IElementPredicate interface looks like this:

public interface IElementPredicate<T extends Element> {
    public boolean match(T e);
    public String getDescription();
    public Class<T> getGenericClass();
}

The point here is that the predicate interface is expanded to provide the Class object instead. It makes writing the actual findAll method a little bit more work and it adds a bit more work in writing the predicate, but those are both essentially tiny "one-time" things and it makes the query call so much nicer because you don't have to add the extra (potentially redundant) argument, e.g.

List<Declaration> decls = scope.findAll(new DeclarationPredicate());

I haven't noticed this pattern before. Is this a typical way of dealing with the semantics of Java generics? Just curious if I'm missing some better pattern.

Commments?

UPDATE:

One question was what do you need the Class for? Here is the implementation of findAll:

public <T extends Element> List<T> findAll(IElementPredicate<T> pred) {
    List<T> ret = new LinkedList<T>();
    Class<T> c = pred.getGenericClass();
    for(Element e: elements) {
        if (!c.isInstance(e)) continue;
        T obj = c.cast(e);
        if (pred.match(obj)) {
            ret.add(c.cast(e));
        }
    }
    return ret;
}

While it is true that match only takes a T, I need to make sure the object is a T before I can call it. For that, I need the "isInstance" and "cast" methods of Class (as far as I can tell).

like image 654
Michael Tiller Avatar asked May 15 '09 14:05

Michael Tiller


People also ask

Are generics used for type safety?

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.

How generics are type safe in Java?

Generics were added to Java to ensure type safety. And to ensure that generics won't cause overhead at runtime, the compiler applies a process called type erasure on generics at compile time. Type erasure removes all type parameters and replaces them with their bounds or with Object if the type parameter is unbounded.

How do I restrict a generic type in Java?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.


1 Answers

The closest "pattern" I think is type token and Generics Tutorial recommends them. You can also turn base predicate into super-type-token (a.k.a. Gafter gadget) and save the extra couple of lines when defining new predicates.

like image 62
Yardena Avatar answered Sep 30 '22 04:09

Yardena