在Java中,我希望有一些东西:
class Clazz<T> {
static void doIt(T object) {
// shake that booty
}
}
但是我得到了
Cannot make a static reference to the non-static type T
我不了解基本用途之外的泛型,因此无法理解这一点。我无法在互联网上找到有关该主题的更多信息,这无济于事。
有人可以通过类似方式澄清这种用途是否可行?另外,为什么我最初的尝试不成功?
答案 0 :(得分:249)
您不能在静态方法或静态字段中使用类的泛型类型参数。类的类型参数仅在实例方法和实例字段的范围内。对于静态字段和静态方法,它们在类的所有实例之间共享,甚至是不同类型参数的实例,因此显然它们不能依赖于特定的类型参数。
似乎您的问题不应该要求使用类的类型参数。如果您更详细地描述您想要做的事情,也许我们可以帮助您找到更好的方法。
答案 1 :(得分:129)
在实例化类型之前,Java不知道T
是什么。
也许你可以通过调用Clazz<T>.doit(something)
来执行静态方法,但听起来你不能。
处理事情的另一种方法是将type参数放在方法本身中:
static <U> void doIt(U object)
这不能让你对U有正确的限制,但它总比没有好......
答案 2 :(得分:44)
我遇到了同样的问题。我通过在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
}
答案 3 :(得分:14)
在声明doIt()
方法时,可以通过使用通用方法的语法来执行您想要的操作(注意在<T>
和static
之间添加void
方法签名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);
}
}
当我运行它时,会将这些行打印到控制台:
摇动那个战利品'com.eclipseoptions.datamanager.Clazz $ KC'!!! 撼动那个战利品'class com.eclipseoptions.datamanager.Clazz $ SunshineBand'!!!
答案 4 :(得分:7)
我认为还没有提到这种语法(如果你想要一个没有参数的方法):
class Clazz {
static <T> T doIt() {
// shake that booty
}
}
电话:
String str = Clazz.<String>doIt();
希望这有助于某人。
答案 5 :(得分: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由电话,寻呼机和电脑共享,所以os的实际类型是什么?它不能是智能手机,寻呼机和 TabletPC同时出现。因此,您无法创建静态字段 类型参数。
正如克里斯在他的answer中正确指出的那样,你需要在方法中使用type参数,而不是在这种情况下使用类。你可以这样写:
static <E> void doIt(E object)
答案 6 :(得分:4)
其他人已经回答了你的问题,但另外我可以彻底推荐O'Reilly Java Generics一书。这有时候是一个微妙而复杂的主题,如果经常看起来似乎有无意义的限制,但是这本书很好地解释了为什么java泛型是他们的方式。
答案 7 :(得分:3)
以下内容会让您更接近
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());
}
}
答案 8 :(得分:2)
当您为类指定泛型类型时,JVM知道它只有一个类的实例,而不是定义。每个定义只有参数化类型。
泛型像C ++中的模板一样工作,所以你应该首先实例化你的类,然后使用指定类型的函数。
答案 9 :(得分:1)
同样简单来说,它的发生是因为&#34; Erasure&#34; generics的属性。这意味着虽然我们定义ArrayList<Integer>
和ArrayList<String>
,但在编译时它保持两种不同的具体类型,但在运行时JVM擦除泛型类型并只创建一个ArrayList类两节课。因此,当我们为泛型定义静态类型方法或任何内容时,它由该泛型的所有实例共享,在我的示例中,它由ArrayList<Integer>
和ArrayList<String>
共享。这就是为什么你得到错误。在静态上下文中不允许类的泛型类型参数!
答案 10 :(得分:1)
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";
}
}
答案 11 :(得分:0)
由于静态变量由类的所有实例共享。例如,如果您有以下代码
class Class<T> {
static void doIt(T object) {
// using T here
}
}
T仅在创建实例后可用。但是即使在实例可用之前也可以使用静态方法。因此,无法在静态方法和变量中引用泛型类型参数