2012-09-17 80 views
1

试图创建一个通用类型的静态字段不编译:为什么我不能声明一个参数化的静态类变量?

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的作品就好了:

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的番石榴图书馆。

回答

2

编辑:现在我发现问题更好了。

我觉得,首先你必须声明类型为类参数:

class MyClass<Z> { 

获得的知名度,但是现在你不能用它这样的原因是因为静态成员应在班级的所有实例中共享。但是,由于您可以使用不同的类型参数创建实例,因此根据特定类型的静态成员将没有意义。

+1

@马特芬威克:你能告诉静态成员周围的代码?特别是你宣布的那个班级? – Tudor

+0

是 - 已编辑OP。 –

+0

@Matt Fenwick:谢谢,看看我的编辑是否更有意义。 – Tudor

2

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

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可能会因实例而异,因此在static级别有一个E变量是没有意义的。

现在你可以声明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. 在静态字段级别声明的泛型简单地不被允许,因为它们永远不会拥有具体类型的上下文。
+1

值得注意的是,尽管在Java中,GenericType 和GenericType DerivedClass确实是同一类型,但并非所有的面向对象的框架都是这样工作的。在.net(也可能是其他一些框架)中,程序执行过程中使用的每个泛型类型参数组合都会生成一个不同的泛型类型,并带有一组静态变量。任何想从一个框架切换到另一个框架的人都应该意识到这种区别。 – supercat

+0

@supercat好点。 Java的删除最终是为这个告诫负责。很好的指出,由于没有擦除,其他语言可能具有(并且通常具有)不同的语义。谢谢 :) – Brian

1

我认为最简单的答案可能是:虽然JDK编译器在解释泛型时很灵活,但在给定代码语义的情况下修改或指定“Z”类是不可能的。

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

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

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

具体而言,对于Function类,您明确定义了一个约束类:一个以“Z”作为输入,并返回“Z”作为输出。如果你想泛型化这一点,你可以创建一个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; 
         } 
        }; 
      } 
相关问题