Function enumeration

Let's say I have two classes

class A{
    int a;
    int getA(){
        return this.a;
    }
}

      

and

class B extends A{
    String b;
    String getB(){
        return this.b;
    }
}

      

I would like to have an enum containing all possible values โ€‹โ€‹and getters of the inheritance tree (don't ask, I just want to).

enum ValueGetters{
    A("a", A::getA),
    B("b", B::getB);

    String parameterName;
    Function parameterGetter;
}

      

I have a problem writer for this class.

ValueGetters(String parameterName, Function parameterGetter){
    this.parameterName = parameterName;
    this.parameterGetter = parameterGetter;
}

      

returns error for A and B ( Error:(12, 14) java: incompatible types: invalid method reference

).

The same situation for ValueGetters(String parameterName, Function<?,?> parameterGetter)

For ValueGetters(String parameterName, Function<? extends A,?> parameterGetter)

this gives an error only for B, and if I try to overload the constructor with ValueGetters(String parameterName, Function<? extends B,?> parameterGetter)

, I have a new error that both types have the same erasure type.

On the other hand, it ValueGetters(String parameterName, Object parameterGetter)

returns an error indicating that Object

it is not Function

.

I tried to define my own @FunctionalInterface

, but it gave me the same error, and the overloading methods @FunctionalInterface

to accept are both A

and B

apparently not an option (or not obvious).

If anyone could suggest a solution to my problem, I would really appreciate it.

EDITING AND SOLUTION

I know this isn't exactly a rename, but it works the same and provides the functionality I needed:

  • Returns getter
  • Getter is type sensitive
  • The solution does not require any changes to the original class
  • Works well with interfaces

... and now the code:

public class ValueGetter<T extends A, R> {
    private String name;
    private Function<A,R> getter;

    private ValueGetter(String name, Function<A, R> getter){
        this.name = name;
        this.getter = getter;
    }

    private static <T extends A, R> ValueGetter create(String name, Function<T,R> valueGetter){
        return new ValueGetter(name, valueGetter);
    }

    public static ValueGetter<A, Integer> AA = create("a", A::getA);
    public static ValueGetter<B, Integer> BB = create("a", B::getB);

    public Function<T, R> getGetter(){
        return (Function<T, R>) this.getter;
    }
}

      

With this implementation

A a1 = new A(12345);
B b1 = new B(54321, "sdfghjk");
System.out.println(ValueGetter.AA.getGetter().apply(a1));
System.out.println(ValueGetter.AA.getGetter().apply(b1));
System.out.println(ValueGetter.BB.getGetter().apply(b1));

      

compiles and runs, and the line

System.out.println(ValueGetter.BB.getGetter().apply(a1));

      

gives compilation error apply(B) in function cannot be applied to (A)

+3


source to share


2 answers


One of them to implement is using a singleton instance of each class:

class BB {  // Renamed B to BB to make it work with enum
    String b;
    private static final BB instance = new BB();


    public static BB getInstance() {
        return instance;
    }


    public String getB(){
        return this.b;
    }

    // Example usage with cast
    public String getB(Object b) {
        return ((BB) b).getB();
    }
}

public enum ValueGetters{
    B("b", BB.getInstance()::getB);

    String parameterName;
    Function parameterGetter;

    ValueGetters(String parameterName, Function parameterGetter){
        this.parameterName = parameterName;
        this.parameterGetter = parameterGetter;
    }
}

      



You can use it like this:

ValueGetters.B.getGetter().apply(b);

      

+1


source


You have two main problems in yours enum

. First, there is a name clash. Your classes are named A

and B

and your constants enum

are named A

and B

. In all contexts where an identifier can refer to both classes or a variable, Java will prefer variable, hence you are referring to constants enum

A

and B

that are in scope with your A::getA

and B::getB

.

Also, you are using a raw type Function

that does not support such specific method references. One way to fix this,

package somepackage;

import java.util.function.Function;

class A{
    int a;
    int getA(){
        return this.a;
    }
}
class B extends A{
    String b;
    String getB(){
        return this.b;
    }
}
enum ValueGetters{
    A("a", somepackage.A::getA),
    B("b", somepackage.B::getB);

    String parameterName;
    Function parameterGetter;

    <T> ValueGetters(String pName, Function<T,Object> f) {
        parameterName=pName;
        parameterGetter=f;
    }
}

      

Note that you need the classes to be named package

in order to be able to figure out the names. There is no way to do this for the default unnamed package.

In addition, the variable parameterGetter

is still of type raw Function

, since there is no way to express the common function type for your two functions that have the A -> Integer

and signatures B -> String

. You cannot solve this with Generics here as they enum

do not support different type parameters in constants.




You've already found the right solution by moving from a enum

generic class that can define differently typed constants. Naming constants with AA

and BB

instead of A

and B

also resolves the naming conflict. But you shouldn't use Function<A,R>

when it is implied Function<T,R>

, besides, you have copy and paste errors.

You can clean up this by simplifying your solution:

// you may also declare it as  public class ValueGetter<T extends A, R>,
// but there would be no actual benefit...
public final class ValueGetter<T, R> {
    private String name;
    private Function<T,R> getter;

    private ValueGetter(String name, Function<T, R> getter){
        this.name = name;
        this.getter = getter;
    }
    public static final ValueGetter<A, Integer> AA = new ValueGetter<>("a", A::getA);
    public static final ValueGetter<B, String>  BB = new ValueGetter<>("b", B::getB);

    public Function<T, R> getGetter() {
        return this.getter;
    }
    public String name() {
        return name;
    }
}

      

+1


source







All Articles