Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

passing and enforcing a member function in java

I'm trying to create a work queue class (FooQueue) that has:

  • a set of function members doing work (do*). Each one of them take one parameter, having the same type (FooItem).
  • an 'add' function that takes 2 parameters: one of the above functions, and a FooItem. Most importantly, the add function should only be able to take in a member function of the FooQueue class, rather than a member function of another class with the similar signature

The code compiles when the do function is static, but not when the function is non-static, even though acording to this the definition of the instance function member is correct.

What should be changed to make it compile/run?

public class App {
    public static void main( String[] args ) {
        FooQueue q = new FooQueue();
        q.add( FooQueue::dos, new FooItem() );    // this compiles
        q.add( q::do1, new FooItem() );           // this does not:
                                                  // does not consider q::do1 'delegate'
                                                  // as taking 2 parameters,
                                                  // with q being the first one
        FooQueue q2 = new FooQueue2();
        q.add( FooQueue2::dos, new FooItem() );   // want this to give compiler error
        q.add( FooQueue2::do1, new FooItem() );   // want this to give compiler error
    }
}

public class FooQueue {
    public static void dos( FooQueue q, FooItem item ) {
        System.out.println( "FooQueue:sdo" );
    }
    public void do1( FooItem item ) {
        System.out.println( "FooQueue:do1" );
    }
    public void add( java.util.function.BiConsumer<FooQueue,FooItem> func, FooItem wi ) {
        System.out.println( "FooQueue:addWorkItem2" );
        func.accept( this, wi );
    }
}
public class FooItem {
}
public class FooQueue2 {
    public static void dos( FooQueue2 q2, FooItem item ) {
        System.out.println( "FooQueue2:sdo" );
    }
    public void do1( FooItem item ) {
        System.out.println( "FooQueue2:do1" );
    }
}
like image 565
daniel Avatar asked Mar 05 '17 11:03

daniel


1 Answers

It's not related to static / non-static method, nor generics, but only to BiConsumer definition.

BiConsumer requires two parameters, so you need lambda that requires two parameters and doesn't return any.

To fix that, use instance method reference:

FooQueue q = new FooQueue();
q.add(FooQueue::do1, new FooItem());

Don't confuse it with static method reference. FooQueue::do1 is syntatic sugar for lambda:

(qInstance, item) -> qInstance.do1(item));

This approach allows you to accept only methods from FooQueue.

Note that q:do1 is not compatible with BiConsumer as it's converted to:

(item) -> q.do1(item)

Read more about Instance method reference


Full example with different classes

public class App {
    public static void main(String[] args) {
        FooQueue q = new FooQueue();
        FooQueue2 q2 = new FooQueue2();

        q.add(FooQueue::do1, new FooItem());
        // Equals to:
        q.add((qInstance, item) -> qInstance.do1(item), new FooItem());

        // q.add(FooQueue2::do1, new FooItem());  // not compile
    }
}

class FooQueue {
    void do1(FooItem item) {
        System.out.println("FooQueue:do1");
    }

    void add(BiConsumer<FooQueue, FooItem> func, FooItem wi) {
        System.out.println("FooQueue:addWorkItem");
        func.accept(this, wi);
    }
}

// class that pretends to be FooQueue
class FooQueue2 {
    void do1(FooItem item) {
        System.out.println("FooQueue2:do1");
    }
}

class FooItem {
}
like image 63
Piotr Lewandowski Avatar answered Oct 03 '22 00:10

Piotr Lewandowski