2015-01-15 2 views
4

Линия, в которой я создаю массив, дает мне предупреждение Generic array creation. Что такое хороший способ справиться с этим?Создание общего массива внутреннего класса

public class Foo<T> { 

    void someMethod() { 
     Point[] points = new Point[3]; 
    } 

    class Point { 
     float x, y; 
    } 
} 
+7

класс 'Точка статический {...'. Похоже, что ваш внутренний класс просто предназначен для хранения 'x' и' y', и на самом деле не нужен скрытый указатель на экземпляр 'Foo '. Если я прав, то это должен быть вложенный класс, а не внутренний класс. – ajb

+1

@ajb Это должен быть ответ (потому что это ответ). –

+1

Интересно, почему 'Point p = new Point()' компилируется отлично, а 'Point [] points = new Points [3]' does not. – Pshemo

ответ

8

Первого, давайте выяснить причину, почему Java считает, что new Point[3] создает общий массив, в то время как Point, как представляется, не шаблонный класс. Это происходит потому, что Point - это нестатический класс, что означает, что он содержит скрытую ссылку на Foo<T>, встроенную компилятором. Класс выглядит это Java:

class Foo$Point<T> { 
    Foo<T> _hidden_Foo; 
    float x, y; 
} 

Foo$, <T> и _hidden_Foo не существует в тексте вашей программы, но компилятор считает, что они есть, потому что Point является внутренний класс универсального класса Foo<T> ,

Есть два способа исправления этой проблемы:

  • Вы могли бы сделать static свой класс Point, предполагая, что это то, что вы намеревались сделать. См. Ответ ajb. Тем не менее, любые методы экземпляра Point больше не смогут получить доступ к Foo<T>
  • Если static не является вариантом, замените массив на List<Point> или другой набор, соответствующий вашим потребностям. Ограничение распространяется только на общие массивы, но общие коллекции в порядке.

Вот как вы можете использовать коллекцию:

public class Foo<T> { 
    void someMethod() { 
     List<Point> points = new ArrayList<Point>(); 
     ... // add three points to the list 
    } 
    class Point { 
     float x, y; 
    } 
} 
+0

Я не уверен насчет 'Foo $ Point '. Если я не ошибаюсь, он объявит 'T' в' Point', который будет отличаться от 'Foo '. У вас есть источник, который мог бы подтвердить его? – Pshemo

+0

@Pshemo Я думаю, что 'T' в' Foo $ Point 'не зависит от' T' в 'Foo '. Я помещаю '' для иллюстрации того, почему 'Point' становится общим, делая вид, что это класс верхнего уровня. Я не собирался предполагать, что компилятор Java будет генерировать код точно так же, только (1) будет ссылка на «Foo » внешнего контекста и (2), поэтому класс «Point» будет рассмотрен родовой. – dasblinkenlight

3

Мне кажется, что ваш Point класс только там провести x и y, и нет никаких оснований для того, чтобы иметь скрытую ссылку на экземпляр Foo<T>. Если это правильно, то Point должен быть вложенным классом, а не внутренним классом. Добавьте static ключевое слово:

public class Foo<T> { 

    void someMethod() { 
     Point[] points = new Point[3]; 
    } 

    static class Point { 
     float x, y; 
    } 
} 
2

Внутренние классы также имеют доступ к непатентованным типу своего внешнего класса. Допустим, что мы имеем

class Foo<T> { 

    class Point { 
     float x, y; 
     T value; 
     T getValue(){ 
      return value; 
     } 
    } 
} 

При создании экземпляра Foo как

Foo<String> f = new Foo<>(); 

мы можем создать экземпляр своего внутреннего класса на основе его внешнего экземпляра как

Point p = f.new Point(); 
// or 
//Foo<String>.Point p = f.new Point 
// if we are creating it for instance outside of Foo class 

и компилятор знаю, что p.getValue() возвращает String, поэтому он позволяет нам использовать p.getValue().charAt(0).

Сейчас проблема заключается в том, что generic type can't be used in any part of array type, что означает, что мы не можем использовать:

  • T[size].
  • Foo<T>[size]
  • или даже не Foo<T>.Point[size]

Последний пример, кажется, ваш случай, потому что

Point[] points = new Point[3]; 

является эквивалентом

Point[] points = new Foo<T>.Point[3]; 
// Foo<T> is type of outer instance on which you are invoking new 

У вас есть несколько вариантов решения этой проблемы.

  1. Вы можете явно сказать, что вы не хотите использовать общий тип, написав

    Point[] points = new Foo.Point[3];// we got rid of <T> 
    

    , но не делают этого, потому что raw types are evil.

  2. Лучшее решение - избегать массивов и использовать коллекцию, которая поддерживает дженерики, такие как List<Point>.

    List<Point> points = new ArrayList<>(); 
    
  3. Но, вероятно, лучшим решением будет просто избавиться от зависимости от T от внешнего класса Foo. Это может быть достигнуто путем создания вашего внутреннего класса static, а это означает, что ему не потребуется экземпляр его внешнего класса, поэтому ему не нужно знать, какой общий тип используется им.
    Таким образом, вы можете просто использовать

    static class Point { 
        float x, y; 
    } 
    

    и теперь

    Point[] points = new Point[3]; 
    

    будет компилироваться.

0

Point - нестатический внутренний класс. Таким образом, Point, написанный сам по себе, означает Foo<T>.Point, параметризованный тип. Вы не можете сделать new Point[3] (что то же самое, что и new Foo<T>.Point[3]), по этой же причине вы не можете сделать new ArrayList<T>[3].

Так давайте возьмем аналогию и спросить, что вы делаете, когда вы хотите сделать

ArrayList<T>[] lists = new ArrayList<T>[3]; 

Есть два способа:

  1. Создать массив исходного типа:

    ArrayList<T>[] lists = new ArrayList[3];

  2. Или, если вам не нравятся сырые типы, создать массив подстановочного-параметрироваться типа:

    ArrayList<T>[] lists = (ArrayList<T>[])new ArrayList<?>[3];

Таким образом, в нашем случае, мы имеем те же два решения:

  1. Создать массив исходного типа. Однако, что такое сырой тип? Это не Point, как мы нашли; потому что это неявно параметризовано. Вместо этого мы должны явно квалифицировать имя с именами внешних классов: Foo.Point:

    Point[] points = new Foo.Point[3];

  2. Или, если вам не нравятся сырые типы, создать массив подстановочного-параметризованных типа:

    Point[] lists = (Point[])new Foo<?>.Point[3];

Смежные вопросы