在Java中,我想要一些东西:
class Clazz<T> {
static void doIt(T object) {
// ...
}
}
但我明白了
无法对非静态类型 T 进行静态引用
除了基本用途之外,我不了解泛型,因此对此没有多大意义。我无法在互联网上找到有关该主题的大量信息,这无济于事。
有人可以通过类似的方式澄清这种使用是否可能吗?另外,为什么我最初的尝试没有成功?
在Java中,我想要一些东西:
class Clazz<T> {
static void doIt(T object) {
// ...
}
}
但我明白了
无法对非静态类型 T 进行静态引用
除了基本用途之外,我不了解泛型,因此对此没有多大意义。我无法在互联网上找到有关该主题的大量信息,这无济于事。
有人可以通过类似的方式澄清这种使用是否可能吗?另外,为什么我最初的尝试没有成功?
您不能在静态方法或静态字段中使用类的泛型类型参数。类的类型参数仅在实例方法和实例字段的范围内。对于静态字段和静态方法,它们在类的所有实例之间共享,甚至是不同类型参数的实例,因此显然它们不能依赖于特定的类型参数。
您的问题似乎不需要使用类的类型参数。如果您更详细地描述您正在尝试做的事情,也许我们可以帮助您找到更好的方法来做到这一点。
T
在您实例化一个类型之前,Java 不知道是什么。
也许你可以通过调用来执行静态方法,Clazz<T>.doit(something)
但听起来你不能。
另一种处理方式是将类型参数放在方法本身中:
static <U> void doIt(U object)
这不会让你对 U 有正确的限制,但总比没有好....
我遇到了同样的问题。Collections.sort
我通过下载java 框架中的源代码找到了答案。我使用的答案是将<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
}
我认为这种语法还没有被提及(如果你想要一个没有参数的方法):
class Clazz {
static <T> T doIt() {
// shake that booty
}
}
和电话:
String str = Clazz.<String>doIt();
希望这对某人有所帮助。
在声明方法时,可以使用泛型方法的语法来做你想做的事情doIt()
(注意在方法签名中添加了<T>
betweenstatic
和):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);
}
}
当我运行它时,它将这些行打印到控制台:
动摇那个战利品'class com.eclipseoptions.datamanager.Clazz$KC'!!!
动摇那个战利品'class com.eclipseoptions.datamanager.Clazz$SunshineBand'!!!
错误中正确提到了它:您不能对非静态类型 T 进行静态引用。原因是类型参数T
可以被任何类型参数替换,例如Clazz<String>
等Clazz<integer>
。但是静态字段/方法由所有非静态类型共享- 类的静态对象。
以下摘录摘自文档:
类的静态字段是类的所有非静态对象共享的类级变量。因此,不允许使用类型参数的静态字段。考虑以下类:
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的实际类型是什么?它不能同时是智能手机、寻呼机和平板电脑。因此,您不能创建类型参数的静态字段。
正如克里斯在他的回答中正确指出的那样,在这种情况下,您需要将类型参数与方法一起使用,而不是与类一起使用。你可以这样写:
static <E> void doIt(E object)
像下面这样的东西会让你更接近
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());
}
}
由于静态变量由类的所有实例共享。例如,如果您有以下代码
class Class<T> {
static void doIt(T object) {
// using T here
}
}
T 仅在创建实例后可用。但是静态方法甚至可以在实例可用之前使用。因此,不能在静态方法和变量中引用泛型类型参数
当你为你的类指定一个泛型类型时,JVM 只知道它有你的类的一个实例,而不是定义。每个定义只有参数化类型。
泛型就像 C++ 中的模板一样工作,因此您应该首先实例化您的类,然后使用指定类型的函数。
简而言之,它的发生是因为泛型的“擦除”属性。这意味着虽然我们定义了ArrayList<Integer>
and ArrayList<String>
,但在编译时它仍然是两种不同的具体类型,但在运行时 JVM 会擦除泛型类型和只创建一个 ArrayList 类而不是两个类。因此,当我们为泛型定义静态类型方法或任何东西时,它由该泛型的所有实例共享,在我的示例中,它由ArrayList<Integer>
和共享。这ArrayList<String>
就是你得到错误的原因。类的泛型类型参数不是允许在静态上下文中!
@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";
}
}
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
}