Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 method references : validation of methods at compile time

Tags:

java

java-8

I'd like to use the new method references of Java 8 to provide more validation of some code at compile time.

Let's say I have a validateMethod method which requires one parameter : a "method" to be validated. For example :

validateMethod(foo, "methodA");

Here, the method would validate that foo#methodA() exists, at runtime.

Using method references, I'd like to be able to do :

validateMethod(foo::methodA);

So the existence of the method would be validated at compile time.

The problem is that it seems method references have to be assigned to a functional interface. For example, this :

Object dummy = foo::methodA;

Generates the error : "The target type of this expression must be a functional interface".

If I create a functional interface that has a compatible signature with the methodA method, it works :

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
MyFunctionalInterface dummy = foo::methodA;

Now the existence of foo#methodA() is validated at compile time, which is what I want!

But...

Let's say validateMethod doesn't know the signature of the method it has to validate. Is it still possible to implement it then?

Let's pretend we don't care about ambiguity and overloaded methods. Is it possible in Java 8 to implement some kind of method which would trigger the validation of any method reference?

For example :

public class Foo
{
    public String methodA()
    {
        return "methodA";
    }

    public String methodB(String str)
    {
        return "methodB";
    }

    public String methodC(String str, int nbr)
    {
        return "methodC";
    }
}

Foo foo = new Foo();
validateMethod(foo::methodA); // Compile
validateMethod(foo::methodB); // Compile
validateMethod(foo::methodC); // Compile
validateMethod(foo::methodD); // Error!

Would it be possible to implement validateMethod in such a way that any method reference would be accepted, so the existence of the method would be validated at compile time?

I tried :

public void validateMethod(Object obj){}

But it doesn't work : "The target type of this expression must be a functional interface"

This would work :

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
public void validateMethod(MyFunctionalInterface param){}

But only for methodA of the Foo class, because its signature (no parameter) is compatible with the functional interface's method signature!

Would it be possible to implement the functional interface MyFunctionalInterface in such a way that any method reference would be a valid parameter and therefore would be validated at compile time?

Any other ways you see to validate the existence of a method at compile time?

like image 553
electrotype Avatar asked Dec 30 '13 13:12

electrotype


People also ask

How many types of method references are there in Java 8?

Types of Method References Java 8 introduced typically with 4 types of Method References. The following are the types and will write example programs on each type. A) Reference to a Static Method B) Reference to instance method from instance - ClassInstance::instanceMethodName

What is method reference in Java?

Method reference is used to refer method of functional interface. It is compact and easy form of lambda expression. Each time when you are using lambda expression to just referring a method, you can replace your lambda expression with method reference. In this tutorial, we are explaining method reference concept in detail.

How to refer a static method in Java?

Reference to an instance method. Reference to a constructor. You can refer to static method defined in the class. Following is the syntax and example which describe the process of referring static method in Java. In the following example, we have defined a functional interface and referring a static method to it's functional method say ().

What is instance method reference in Java?

When we refer an instance method using the method reference concept then it is called instance method reference. We use a class object to refer to the instance method. The syntax of instance method reference is given below.


2 Answers

You seem to be trying to use method references, which are really the short-hands for lambda expressions, as method literals, which are the syntactic references to methods (much like Foo.class is the syntactic reference to class instance of Foo). These two are not the same, and this is the reason for the impedance you encounter. Things you try are the abuse of language feature which javac compiler utterly resists.

Unfortunately, there is no method literals in Java, so you will have to describe the method by other means, e.g. Reflection, MethodHandles.Lookup, etc. I think it is very easy to come up with the reflective checker for this kind of thing, or even build up the annotation processor to check the existence of given methods in compile time.

like image 54
Aleksey Shipilev Avatar answered Nov 07 '22 00:11

Aleksey Shipilev


You could try something like the following:

public class Validate {
    public String methodA() { return "methodA"; }
    public String methodB(String s) { return "methodB"; }
    public String methodC(String s, int n) { return "methodC"; }

    public static void main(String[] args) {
        Validate foo = new Validate();
        validateMethod(foo::methodA);
        validateMethod(foo::methodB);
        validateMethod(foo::methodC);
    }

    private interface Func0 { void method(); }
    private interface Func1<T> { void method(T t); }
    private interface Func2<T, U> { void method(T t, U u); }
    private interface Func3<T, U, V> { void method(T t, U u, V v); }

    public static void validateMethod(Func0 f) { }
    public static <T> void validateMethod(Func1<T> f) { }
    public static <T, U> void validateMethod(Func2<T, U> f) { }
    public static <T, U, V> void validateMethod(Func3<T, U, V> f) { }
}

But you'll need to provide an interface and an overload of validateMethod for every arity of method you need to validate. Also, it will not work if the method to validate is overloaded, unless you add an explicit cast:

    // if there are two methodA's:
    public String methodA() { return "methodA"; }
    public String methodA(long x) { return "methodA"; }

        validateMethod(foo::methodA); // this doesn't work
        validateMethod((Func0)foo::methodA); // this does
        validateMethod((Func1<Long>)foo::methodA); // so does this
like image 23
David Conrad Avatar answered Nov 07 '22 01:11

David Conrad