Статический метод в общем классе?

В Java, я хотел бы иметь что-то как:

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

Но я получаю

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

static <U> void doIt(U object)

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

Ответ 3

Я столкнулся с этой проблемой. Я нашел ответ, загрузив исходный код для Collections.sort в java framework. Ответ, который я использовал, заключается в том, чтобы поместить genaric в метод, а не в определение класса.

Итак, это сработало:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

Конечно, после прочтения ответов выше я понял, что это будет приемлемой альтернативой, не используя класс genaric:

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}

Ответ 4

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

Ответ 5

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

Ответ 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 одновременно. Следовательно, вы не можете создавать статические поля параметров типа.

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

static <E> void doIt(E object) 

Ответ 7

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

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

EDIT: обновленный пример с более подробной информацией

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

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

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

И вызов:

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

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

Ответ 9

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

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

Ответ 10

Иными словами, это происходит из-за свойства "Erasure" для генериков. Это означает, что, хотя мы определяем ArrayList<Integer> и ArrayList<String>, во время компиляции он остается как два разных конкретных типа, но во время выполнения JVM стирает родовые типы и создает только один класс ArrayList вместо двух классов. Поэтому, когда мы определяем метод статического типа или что-либо для общего типа, он разделяется всеми экземплярами этого общего типа, в моем примере он разделяется как ArrayList<Integer>, так и ArrayList<String>. Вот почему вы получаете ошибку. Общий тип Параметр класса не допускается в статическом контексте!

Ответ 11

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

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