泛型类中的静态方法?
Posted
技术标签:
【中文标题】泛型类中的静态方法?【英文标题】:Static method in a generic class? 【发布时间】:2010-10-30 11:58:51 【问题描述】:在 Java 中,我想要一些东西:
class Clazz<T>
static void doIt(T object)
// ...
但我明白了
无法对非静态类型 T 进行静态引用
除了基本用途之外,我不了解泛型,因此无法理解这一点。我无法在互联网上找到有关该主题的大量信息,这无济于事。
有人能以类似的方式澄清这种用途是否可能吗?另外,为什么我最初的尝试没有成功?
【问题讨论】:
【参考方案1】:您不能在静态方法或静态字段中使用类的泛型类型参数。类的类型参数仅在实例方法和实例字段的范围内。对于静态字段和静态方法,它们在类的所有实例之间共享,即使是不同类型参数的实例,所以它们显然不能依赖于特定的类型参数。
您的问题似乎不需要使用类的类型参数。如果您更详细地描述您正在尝试做的事情,也许我们可以帮助您找到更好的方法。
【讨论】:
赞成,这个答案实际上解释了发布者的问题,而不仅仅是提供解决方法。 @Andre:你的直觉并非毫无根据; C# 确实以这种方式对待泛型。 "对于静态字段和静态方法,它们在类的所有实例之间共享,甚至是不同类型参数的实例......" 哎哟!再次被类型擦除踢到了疯狂! 如果你看一下泛型类/方法在编译后的样子,你会看到泛型属性被删除了。编译后的 List在您实例化一个类型之前,Java 不知道 T
是什么。
也许你可以通过调用Clazz<T>.doit(something)
来执行静态方法,但听起来你不能。
另一种处理方式是将类型参数放在方法本身中:
static <U> void doIt(U object)
这不会让你对 U 有正确的限制,但总比没有好......
【讨论】:
然后我会调用该方法而不指定任何约束,即 Clazz.doIt(object) 而不是 Clazz 第二种语法更准确,如果编译器无法从调用方法的上下文中推断返回值的类型,则需要使用它。换句话说,如果编译器允许 Clazz.doIt(object),那么就这样做。 我试过 Clazz 使用 Clazz. 相比,我认为这是一种奇怪的语法 你为什么说它没有让你对 U 有正确的限制?【参考方案3】:我遇到了同样的问题。我通过在 java 框架中下载Collections.sort
的源代码找到了答案。我使用的答案是将<T>
泛型放在方法中,而不是在类定义中。
这样就成功了:
public class QuickSortArray
public static <T extends Comparable> void quickSort(T[] array, int bottom, int top)
//do it
当然,在阅读了上面的答案后,我意识到如果不使用泛型类,这将是一个可以接受的替代方案:
public static void quickSort(Comparable[] array, int bottom, int top)
//do it
【讨论】:
虽然接受的答案在技术上是正确的:这实际上是我在谷歌引导我提出这个问题时所寻找的。span> Props 提供一个包含T extends XXX
语法的示例。
@Chris 因为无论如何你都在使用泛型,所以你最好一直使用它们——即不要使用像Comparable
这样的原始类型。请改用<T extends Comparable<? super T>>
。【参考方案4】:
我认为这个语法还没有被提及(如果你想要一个没有参数的方法):
class Clazz
static <T> T doIt()
// shake that booty
然后打电话:
String str = Clazz.<String>doIt();
希望这对某人有所帮助。
【讨论】:
实际上(我不知道 Java 版本),即使没有<String>
也是可能的,因为它只是推断类型参数,因为您将其分配给变量。如果你不分配它,它只是推断Object
。
这是一个完美的解决方案。【参考方案5】:
在声明doIt()
方法时,可以使用泛型方法的语法来做你想做的事(注意static
和void
之间的<T>
在doIt()
的方法签名中添加) :
class Clazz<T>
static <T> void doIt(T object)
// shake that booty
我让 Eclipse 编辑器接受上面的代码,没有出现Cannot make a static reference to the non-static type T
错误,然后将其扩展为以下工作程序(完成了一些适合年龄的文化参考):
public class Clazz<T>
static <T> void doIt(T object)
System.out.println("shake that booty '" + object.getClass().toString()
+ "' !!!");
private static class KC
private static class SunshineBand
public static void main(String args[])
KC kc = new KC();
SunshineBand sunshineBand = new SunshineBand();
Clazz.doIt(kc);
Clazz.doIt(sunshineBand);
当我运行它时将这些行打印到控制台:
摇晃那个战利品'class com.eclipseoptions.datamanager.Clazz$KC' !!! 动摇那个战利品'class com.eclipseoptions.datamanager.Clazz$SunshineBand'!!!
【讨论】:
这种情况下,第二个<T>
屏蔽第一个class C int x; C(int x) ...
中的参数x
屏蔽字段x
的方式相同。
如何解释样本的输出:似乎确实涉及两种类型,一种是通过参数T值?如果 T 真的隐藏了类类型,我会期望“摇晃战利品 'class com.eclipseoptions.datamanager.Clazz” 输出两次没有参数。
与遮罩无关。静态方法根本无法被类泛型类型所绑定。不过它可以定义自己的泛型类型和边界。
有没有一种方法可以定义一次静态类型并为多个静态方法重用它?当我想做static <E extends SomeClass>; //static generic type defined only once and reused
static E foo(E input)return input;
static E bar(E input)return input;
//...etc...
【参考方案6】:
错误中正确提及:您不能对非静态类型 T 进行静态引用。原因是类型参数 T
可以替换为任何类型参数,例如Clazz<String>
或 Clazz<integer>
等。但静态字段/方法由类的所有非静态对象共享。
以下摘录自doc:
一个类的静态字段是一个类级别的变量,被所有人共享 类的非静态对象。因此,类型的静态字段 不允许使用参数。考虑以下类:
public class MobileDevice<T> private static T os; // ...
如果允许类型参数的静态字段,那么下面的代码就会混淆:
MobileDevice<Smartphone> phone = new MobileDevice<>(); MobileDevice<Pager> pager = new MobileDevice<>(); MobileDevice<TabletPC> pc = new MobileDevice<>();
因为静态字段os是phone、pager、pc共享的,那么os的实际类型是什么?它不能是智能手机、寻呼机和 同时平板电脑。因此,您不能创建静态字段 类型参数。
正如克里斯在他的answer 中正确指出的那样,在这种情况下,您需要将类型参数与方法一起使用,而不是与类一起使用。你可以这样写:
static <E> void doIt(E object)
【讨论】:
【参考方案7】:类似下面的东西会让你更接近
class Clazz
public static <U extends Clazz> void doIt(U thing)
编辑:更详细的更新示例
public abstract class Thingo
public static <U extends Thingo> void doIt(U p_thingo)
p_thingo.thing();
protected abstract void thing();
class SubThingoOne extends Thingo
@Override
protected void thing()
System.out.println("SubThingoOne");
class SubThingoTwo extends Thingo
@Override
protected void thing()
System.out.println("SuThingoTwo");
public class ThingoTest
@Test
public void test()
Thingo t1 = new SubThingoOne();
Thingo t2 = new SubThingoTwo();
Thingo.doIt(t1);
Thingo.doIt(t2);
// compile error --> Thingo.doIt(new Object());
【讨论】:
完全按照 Jason S 的建议。 @Andre 之前的建议没有提供 U 必须扩展 Clazz 的限制 我明白了,但除了约束之外,它没有添加任何有用的东西。在我原来的帖子中,我希望能够在不将 U 作为参数传递的情况下做到这一点。 @Andre 请参阅上面的更新。泛型只在方法定义中定义,调用方法时不必传入 @ekj 谢谢,我现在明白了。对不起,我在三年前提出这个问题,当时我每天都在做 Java。我明白了你的想法,尽管我认为你确实明白这并不是我最初的意图。不过,我喜欢你的回答。【参考方案8】:因为静态变量由类的所有实例共享。例如,如果您有以下代码
class Class<T>
static void doIt(T object)
// using T here
T 仅在创建实例后可用。但是静态方法甚至可以在实例可用之前使用。所以,泛型类型参数不能在静态方法和变量中被引用
【讨论】:
【参考方案9】:当你为你的类指定一个泛型类型时,JVM 只知道它有你的类的一个实例,而不是定义。每个定义只有参数化类型。
泛型的工作方式类似于 C++ 中的模板,因此您应该首先实例化您的类,然后使用指定类型的函数。
【讨论】:
Java 泛型与 C++ 模板有很大不同。泛型类是自己编译的。事实上,C++ 中的等效代码可以工作(调用代码看起来像Clazz<int>::doIt( 5 )
)
我比接受的答案更喜欢这个答案...除了 C++ 模板参考之外,关于泛型类型的评论仅适用于类的一个实例而不是整个类。接受的答案没有解释这一点,只是提供了一个解决方法,它与为什么不能在静态方法中使用类的泛型类型没有任何关系。【参考方案10】:
简单来说,它的发生是因为泛型的“擦除”属性。这意味着虽然我们定义了 ArrayList<Integer>
和 ArrayList<String>
,但在编译时它仍然是两种不同的具体类型,但是在运行时,JVM 会删除泛型类型并只创建一个 ArrayList 类而不是两个类。因此,当我们为泛型定义静态类型方法或任何东西时,它由该泛型的所有实例共享,在我的示例中,它由 ArrayList<Integer>
和 ArrayList<String>
共享。这就是你得到错误的原因。泛型类型静态上下文中不允许使用类的参数!
【讨论】:
【参考方案11】:@BD at Rivenhill:自从这个老问题去年重新引起人们的注意,让我们继续讨论一下,只是为了讨论。
doIt
方法的主体根本不做任何T
特定的事情。这里是:
public class Clazz<T>
static <T> void doIt(T object)
System.out.println("shake that booty '" + object.getClass().toString()
+ "' !!!");
// ...
因此您可以完全删除所有类型变量而只需编写代码
public class Clazz
static void doIt(Object object)
System.out.println("shake that booty '" + object.getClass().toString()
+ "' !!!");
// ...
好的。但是让我们回到更接近最初的问题。类声明中的第一个类型变量是多余的。只需要方法中的第二个。我们又来了,但这还不是最终的答案:
public class Clazz
static <T extends Saying> void doIt(T object)
System.out.println("shake that booty "+ object.say());
public static void main(String args[])
Clazz.doIt(new KC());
Clazz.doIt(new SunshineBand());
// Output:
// KC
// Sunshine
interface Saying
public String say();
class KC implements Saying
public String say()
return "KC";
class SunshineBand implements Saying
public String say()
return "Sunshine";
但是,这没什么大惊小怪的,因为以下版本的工作方式相同。它所需要的只是方法参数上的接口类型。任何地方都看不到类型变量。这真的是最初的问题吗?
public class Clazz
static void doIt(Saying object)
System.out.println("shake that booty "+ object.say());
public static void main(String args[])
Clazz.doIt(new KC());
Clazz.doIt(new SunshineBand());
interface Saying
public String say();
class KC implements Saying
public String say()
return "KC";
class SunshineBand implements Saying
public String say()
return "Sunshine";
【讨论】:
【参考方案12】:T 不在静态方法的范围内,因此您不能在静态方法中使用 T。您需要为静态方法定义不同的类型参数。我会这样写:
class Clazz<T>
static <U> void doIt(U object)
// ...
例如:
public class Tuple<T>
private T[] elements;
public static <E> Tuple<E> of(E ...args)
if (args.length == 0)
return new Tuple<E>();
return new Tuple<E>(args);
//other methods
【讨论】:
以上是关于泛型类中的静态方法?的主要内容,如果未能解决你的问题,请参考以下文章
Java 泛型泛型简介 ( 泛型类 | 泛型方法 | 静态方法的泛型 | 泛型类与泛型方法完整示例 )