函数枚举

时间:2017-05-18 08:33:06

标签: java enums functional-interface

假设我有两个班级

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

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

我想有一个枚举,包含所有可能的值和继承树的getter(不要问,我只是想)。

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

    String parameterName;
    Function parameterGetter;
}

我在编写这个类的构造函数时遇到了问题。

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

返回A和B(Error:(12, 14) java: incompatible types: invalid method reference)的错误。

同样的情况适用于ValueGetters(String parameterName, Function<?,?> parameterGetter)

对于ValueGetters(String parameterName, Function<? extends A,?> parameterGetter),这仅为B提供错误,如果我尝试使用ValueGetters(String parameterName, Function<? extends B,?> parameterGetter)重载构造函数,则会出现新错误,即两种类型都具有相同类型的擦除。

另一方面,ValueGetters(String parameterName, Object parameterGetter)返回错误,指出Object不是Function

我尝试定义自己的@FunctionalInterface,但它给了我同样的错误,重载@FunctionalInterface的方法以接受AB显然不是选项(或不是一个明显的选项)。

如果有人可以提出我的问题的解决方案,我将非常感激。

编辑&amp;解

我知道它不是一个枚举,但它以相同的方式工作并提供我需要的功能:

  1. 返回getter
  2. Getter是类型敏感的
  3. 解决方案不需要对原始类进行任何更改
  4. 适用于接口
  5. ......现在,代码:

    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;
        }
    }
    

    有了这个实现

    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));
    

    编译并工作,而行

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

    给出了编译错误apply(B) in function cannot be applied to (A)

2 个答案:

答案 0 :(得分:1)

实现它的一个问题是使用每个类的单例实例:

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;
    }
}

你可以这样使用它:

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

答案 1 :(得分:1)

您的enum有两个基本问题。首先,有一个名字冲突。您的类名为ABenum常量的名称为AB。在所有上下文中,标识符可以引用类,类或变量,Java会更喜欢变量,因此,您指的是enum常量AB在您的A::getAB::getB

的范围内

此外,您使用的是原始类型 Function,它不支持此类特定方法引用。解决这个问题的一种方法是

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;
    }
}

请注意,您需要将类放在命名的package中才能消除名称的歧义。对于未命名的默认包,无法做到这一点。

此外,parameterGetter变量仍然具有原始类型 Function,因为无法为具有签名{{}的两个函数表达公共函数类型。 1}}和A -> Integer。你不能在这里使用泛型来解决这个问题,因为B -> String不支持在常量上使用不同的类型参数。

您已经找到了正确的解决方案,从enum转到普通的泛型类,它可以定义不同类型的常量。命名常量enumAA而不是BBA也可以解决名称冲突。但是,如果有B,则不应使用Function<A,R>,此外,您还有复制和粘贴错误。

您可以清理它,简化您的解决方案:

Function<T,R>