简体   繁体   中英

Parsing generic functions in Java

So I have this piece of code which uses several maps to store some simple classes. Now I also want to look op some of the values based on for example the x position of this item. I tried to do something with Generics and lambda's, but it just didn't work out. Also if there is an easier way to do this, please tell. I have most of my experience in Python, so this might just not be practical in Java.

interface Compare {
     public <T, P> boolean apply(T obj, P comp);
}

class Utils {
    public static <T, P> List<T> retrieve(Collection<T> args, P value, Compare c) {

        List<T> r = new ArrayList<T>();

        for (T i: args) {
            if (c.apply(i, value)) {
                r.add(i);
            }
        }

        return r;

    }
}

class Point {
    int x;
    int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
}

This doesn't raise any errors as of yet, but when I try to actually use the retrieve function on a HashMap it doesn't work.

Point p = Utils.retrieve(points.values(), 0, (Point p, Integer x) -> {return px == x;});

There are two errors in eclipse:

  1. At Utils.retrieve, eclipse notes that: The method retrieve(Collection, P, Compare) in the type Utils is not applicable for the arguments (Collection, int, (Point p, Integer x) -> {})
  2. At the lambda expression, eclipse notes that: Illegal lambda expression: Method apply of type Compare is

How would one handle this situation? My thanks in advance.

You're fairly close to getting the code you're after.

Firstly, move the generic parameter declaration to the interface (as the esteemed Esquire Wasserman suggests in his comment).

interface Compare<T, P> {
    public boolean apply(T obj, P comp);
}

Now you just need to modify the way you're calling your new toys. I've created a main method with the following code and it works nicely:

public static void main(String[] args) {
    Set<Point> points = new HashSet<>(8);
    points.add(new Point(0, 0));
    points.add(new Point(1, -1));
    points.add(new Point(1, 1));
    points.add(new Point(-1, -1));
    points.add(new Point(-1, -1));
    System.out.println("Set<Point> points:\n" + points);

    Compare<Point, Integer> sameX = (p, x) -> p.x == x;
    List<Point> matchingPoints =
            Utils.<Point, Integer>retrieve(points, 0, sameX);
    System.out.println("\nmatchingPoints:\n" + matchingPoints);
}

I've used a simple Set<Point> here because I don't know what you're intending to store in a Map , but calling Map.values() will produce a Collection<Point> just as well as the Set<Point> does in this example.

The reason this works is that you specify the parameter types to the generic static method in the angle-brackets immediately before the name of the static method: Utils.<Point, Integer>retrieve and you don't put the types inside the lambda. It's also worth saying here that lambda expressions should be as compact as possible, so rather than {return px == x;} you can simply have px == x .

Note, however, that in order to see the output in a readable form, you need to override the toString() method in your Point class:

@Override
public String toString() {
    return "Point(" + x + ", " + y + ")";
}

This will give you the following output:

Set<Point> points:
[Point(-1, -1), Point(0, 0), Point(1, -1), Point(-1, -1), Point(1, 1)]

matchingPoints:
[Point(0, 0)]

(Also note that you don't have to move the lambda out of the call to the retrieve method, but it makes the code tidier, and fits better in this narrow Stack Overflow window.)

Addendum

Your follow-up comment made me realise that your Utils.retrieve method was going to hit trouble unless exactly the right sort of Compare method was passed to it. Rather than leave things open to chance, it's better to lock this down by modifying the signature of your retrieve method so that it becomes this:

public static <T, P> List<T> retrieve(Collection<T> args,
        P value, Compare<T, P> c) {

Now the Compare object which is passed to retrieve must take the same types which apply to the Collection<T> and the value . Once you've made this change you can inline your lambda expression like this:

List<Point> matchingPoints = Utils.<Point, Integer>retrieve(
        points, 0, (p, x) -> p.x == x);

You won't get errors now, because the strict type applied to the Compare parameter in your retrieve method allows the IDE (and compiler) to see that the lambda must be of type <Point, Integer> .

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