Статический метод в универсальном классе? - PullRequest
185 голосов
/ 01 июня 2009

В Java я хотел бы иметь что-то вроде:

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

Но я получаю

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

Я не понимаю дженерики, выходящие за рамки базового использования, и поэтому не могу понять этого. Это не помогает, что я не смог найти много информации в Интернете по этому вопросу.

Может ли кто-нибудь уточнить, возможно ли такое использование подобным образом? Кроме того, почему моя первоначальная попытка оказалась неудачной?

Ответы [ 12 ]

249 голосов
/ 02 июня 2009

Нельзя использовать параметры общего типа класса в статических методах или статических полях. Параметры типа класса находятся только в области видимости для методов экземпляра и полей экземпляра. Для статических полей и статических методов они являются общими для всех экземпляров класса, даже для экземпляров параметров другого типа, поэтому, очевидно, они не могут зависеть от конкретного параметра типа.

Не похоже, что ваша проблема должна требовать использования параметра типа класса. Если вы опишите, что вы пытаетесь сделать более подробно, возможно, мы поможем вам найти лучший способ сделать это.

130 голосов
/ 01 июня 2009

Java не знает, что такое T, пока вы не создадите экземпляр типа.

Возможно, вы можете выполнять статические методы, вызывая Clazz<T>.doit(something), но, похоже, вы не можете.

Другим способом обработки вещей является помещение параметра типа в сам метод:

static <U> void doIt(U object)

, который не дает вам правильного ограничения на U, но лучше, чем ничего ...

44 голосов
/ 25 октября 2011

Я столкнулся с этой же проблемой. Я нашел свой ответ, загрузив исходный код для 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
}
15 голосов
/ 13 мая 2012

Можно сделать то, что вы хотите, используя синтаксис для универсальных методов при объявлении метода 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 '!!!
потряси эту добычу 'класс com.eclipseoptions.datamanager.Clazz $ SunshineBand' !!!

7 голосов
/ 22 апреля 2016

Я думаю, что этот синтаксис еще не упоминался (в случае, если вам нужен метод без аргументов):

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

И звонок:

String str = Clazz.<String>doIt();

Надеюсь, это кому-нибудь поможет.

6 голосов
/ 23 октября 2015

Это правильно упоминается в ошибке: вы не можете сделать статическую ссылку на нестатический тип 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 одновременно. Поэтому вы не можете создавать статические поля параметров типа.

Как правильно указал Крис в своем ответе , вам нужно использовать параметр типа с методом, а не с классом в этом случае. Вы можете написать это как:

static <E> void doIt(E object) 
4 голосов
/ 01 июня 2009

Другие уже ответили на ваш вопрос, но кроме того, я могу полностью порекомендовать книгу O'Reilly Java Generics . Иногда это тонкий и сложный предмет, и если часто кажется, что он имеет бессмысленные ограничения, но книга довольно неплохо объясняет, почему дженерики Java такие, какие они есть.

3 голосов
/ 10 мая 2012

Что-то вроде следующего приблизит вас

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());
    }
}
2 голосов
/ 01 июня 2009

Когда вы указываете универсальный тип для своего класса, JVM знает, что он имеет только экземпляр вашего класса, а не определения. Каждое определение имеет только параметризованный тип.

Обобщения работают как шаблоны в C ++, поэтому вам следует сначала создать экземпляр класса, а затем использовать функцию с указанным типом.

1 голос
/ 12 октября 2017

@ 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()
                       + "' !!!");
  }
// ...
}

Ok. Но вернемся ближе к исходной проблеме. Первая переменная типа в объявлении класса является избыточной. Необходим только второй метод. Здесь мы снова идем, но это еще не окончательный ответ:

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";
      }
}
...