为什么我不能声明参数化的静态类变量?

时间:2012-09-17 16:58:23

标签: java generics static

尝试使用泛型类型创建静态字段无法编译:

class MyClass {

    public static Function<Z, Z> blargh = new Function<Z, Z>() {
        public Z apply(Z a) {
            return a;
        }
    };
}

Eclipse说:

Multiple markers at this line
    - Z cannot be resolved to a type
    - Z cannot be resolved to a type
    - Z cannot be resolved to a type
    - Z cannot be resolved to a type
    - The type new Function<Z,Z>(){} must implement the inherited 
     abstract method Function<Z,Z>.apply(Z)

但是用具体类型替换所有Z s就可以了:

static Function<Integer, Integer> blargh = new Function<Integer, Integer>() {
    public Integer apply(Integer a) {
        return a;
    }
};

这里发生了什么?


上下文:

  1. 我原本试图找出为什么this code使用方法而不是字段:

    public static <T extends Throwable> F<T, String> eMessage() {
      return new F<T, String>() {
        public String f(final Throwable t) {
          return t.getMessage();
        }
      };
    }
    

    也许是为了克服这个限制?

  2. Function类型来自Google的番石榴图书馆。

3 个答案:

答案 0 :(得分:3)

修改:现在我看到问题更好了。

我认为首先你必须将类型声明为类参数:

class MyClass<Z> {

获得可见性,但现在你不能像这样使用它的原因是因为静态成员应该在类的所有实例之间共享。但是,由于您可以创建具有不同类型参数的实例,因此取决于特定类型的静态成员将没有意义。

答案 1 :(得分:3)

您只能在成员字段上使用类级别泛型。例如:

public class MyClass<Z> {
    private Function<Z, Z> function;
    // ...
}

是对的。相反,声明此static将会中断。为什么呢?

想想ArrayList。它的类声明类似于:

public class ArrayList<E> extends AbstractList<E> implements List<E>, ... {
    // ...
}

E在静态意义上没有上下文,因为静态变量属于ArrayList的所有实例,但E对于每个ArrayList实例可能不同:

// Here's one ArrayList with E as String
List<String> strs = new ArrayList<String>();
// And another with E as Boolean
List<Boolean> bools = new ArrayList<Boolean>();

因为E可以从实例更改为实例,所以在E级别拥有static变量没有意义。

现在你可以用泛型声明static方法,但方式完全不同。例如,Collections.sort可以有这样的声明:

public static <T> void sort(List<? extends T> list, Comparator<T> comparator)

请注意,T在返回类型之前被声明为方法的一部分。这是在方法中定义T的上下文,T可能因呼叫而异。

编辑后评论:在您的情况下,您没有在任何地方声明Z,因此无论如何您都无法使用它。请参阅上面的MyClass声明。注意我是如何直接在类上使用<Z>的?这意味着Z将是一些任意类型。

对于您想要弄清楚的内容,您应该将Function视为表示转换的通用方法。让我们剖析您发布的方法:

public static <T extends Throwable> F<T, String> eMessage() {
    return new F<T, String>() {
        public String f(final Throwable t) {
            return t.getMessage();
        }
    };
}

首先,请注意这是一个方法,而不是像您的OP那样的静态字段,因此在此处使用泛型是合法的。此外,它是static,因此需要在返回类型之前声明任何泛型。在此,他们声明<T extends Throwable>,因此T必须是某种延伸Throwable的错误或异常。返回类型为F<T, String>,该函数采用T(a Throwable)并返回String。实际的对象声明了一个f方法,通过调用Throwable.getMessage来实现这一点。由于该项目是functionaljava,所以一切都基于F类,所以泛型无处不在。

请记住:

  1. 在类级别声明的泛型只能由非静态成员和方法使用。
  2. 在方法级别声明的泛型是允许的,但不引用类级别类型,而是引用在返回类型之前声明的类型。
  3. 在静态字段级别声明的泛型不允许,因为它们的具体类型永远不会有上下文。

答案 2 :(得分:1)

我认为最简单的答案可能是:虽然JDK编译器在解释泛型的方式上很灵活,但是不可能修改或指定&#34; Z&#34;给出代码语义的类。

在泛型的所有使用中,您必须定义一个语法,该语法指定正在操作的泛型类的标识。例如(如上例所示)。

1)使用通用的参数化实用程序功能。在这种情况下,它对编译器显而易见,因为指定的类作为输入函数发送。

2)将类本身定义为通用的,非静态的。这将要求类的用户使用适当的指定类参数声明它。

具体来说,对于Function类,您明确定义了一个约束类:一个采用&#34; Z&#34;作为输入,并返回&#34; Z&#34;作为输出。如果要对此进行泛化,可以创建一个FunctionFactory类,该类接受例如Z的单个实例,并返回类型为:

的类型指定函数。
public static <Z> Function<Z,Z> functionFactory(final Z default){
                    return new Function<Z,Z>(){
                        @Override
                        public Z apply(Z input) {
                        // TODO Auto-generated method stub
                        if(input==null)
                                         return default;
                                    else 
                                         return input;
                        }
                    };
            }