泛型类中的静态方法?

时间:2009-06-01 19:35:34

标签: java generics static-methods

在Java中,我希望有一些东西:

class Clazz<T> {
  static void doIt(T object) {
    // shake that booty
  }
}

但是我得到了

Cannot make a static reference to the non-static type T

我不了解基本用途之外的泛型,因此无法理解这一点。我无法在互联网上找到有关该主题的更多信息,这无济于事。

有人可以通过类似方式澄清这种用途是否可行?另外,为什么我最初的尝试不成功?

12 个答案:

答案 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)

Rivenhill的@BD:自去年这个老问题再次受到重视以来,让我们继续讨论,仅仅是为了讨论。 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仅在创建实例后可用。但是即使在实例可用之前也可以使用静态方法。因此,无法在静态方法和变量中引用泛型类型参数