2012-04-26 2 views
1

У меня есть этот класс, только для целей обучения:Java Дженерик (простой случай, ясно загадка для infering)

public class MyClass{ //Looking for a solution without making my class also generic <Type> 

    //Private Arraylist var to hold the value called myvar 

    public MyClass(ArrayList<MyDesiredType> incoming) { 
     //CODE myVar=incoming 
    } 

    public MyDesiredType getType() { 
     return myVar.get(0); 
    } 
} 

Есть ли способ, чтобы вывести в поступающем объекте из конструкторы к типу возвращаемого метода без предупреждений и кастингов и потери типов, но больше всего БЕЗ создания всего класса GENERIC (кажется мне лишним)? Если нет, почему я должен думать, что это невозможно для компилятора?

Это переформулированный вопрос, который я уже сделал, но это был мой первый, и я научился раскрывать его, потому что никто не понимал. Я попытался позже отредактировать исходный вопрос, но все было похоронено. Я изменил и упростил пример и постараюсь сделать это легко. Оригинальный вопрос: Java Generics Silly Thing (Why cant I infer the type?).

Если у вас возникли проблемы, просто сообщите об этом мне, и я удалю его.

+0

Рассматривая старый вопрос и ответы, кажется, совершенно ясно, что то, что вы хотите сделать, невозможно. Реальный вопрос: почему вы не хотите генерировать «MyClass»? – Anthales

+0

Ну, я вижу только один ответ, говорящий о невозможности конструкторского вывода, более половины из них говорят только о дженериках в классе, а другое мы все даем противоречивые ответы на вывод. Я не хочу создавать MyClass generic, потому что хочу знать, существует ли какой-то творческий способ передачи типа изнутри, и потому что я считаю его раздражающим и ненужным, если у вас уже есть тип, чтобы указать его для создания экземпляра класса. И если обычный метод может сделать вывод, почему конструктор не сможет это сделать или передать его частной переменной? – Whimusical

+0

На самом деле есть один случай, когда теоретически невозможно было бы вывести тип, и это будет, если 'incoming' будет' null'. Но компилятор не знает, является ли оно «null», поэтому это может быть один фактический аргумент _why_ вам не допускается вывод типа таким образом. – Anthales

ответ

8

Нет, нет. Как компилятор узнает, какой тип будет возвращен? Общий тип ArrayList в конструкторе не будет известен во время компиляции. Вы либо должны сделать общий класс общим, либо принять другой подход.

Рассмотрим это:

public class Test { 
    public static void main(String[] args) { 
     List<String> arrList = new ArrayList<String>(); 
     arrList.add("FOO"); 
     Test test = new Test(arrList); 
     String testStr = test.returnWhat(); 
     System.out.println("testStr"); 
    } 

    private final List myList; //warning 

    public <T> Test(List<T> ttype) { 
     myList = ttype; 
    } 

    public <T> T returnWhat() { 
     return (T) myList.get(0); //warning 
    } 
} 

Это работает, но дает предупреждения на размеченные линии. Таким образом, на самом деле нет способа добиться того, что вы описываете, не создавая общий класс. Потому что, если:

public class Test { 


public static void main(String[] args) { 
     List<String> arrList = new ArrayList<String>(); 
     arrList.add("FOO"); 
     Test test = new Test(); // now what? 
     String testStr = test.returnWhat(0); // no warning... 
     JPanel p = test.returnWhat(0); // goes through without warning, real nice... 
     test.returnWhat(0); // returns Object 

     Test test2 = new Test(arrList); 
     test2.addElement(new Object()); // boom, inserted object into list of string. 
     String nono = test2.returnWhat(1); // the universe goes down. assign an object to string without warning. even 
              // though one COULD think the class is generic. 
    } 

    // private List<T> myList = new ArrayList<T>(); compiler error, T is unknown 
    private List myList = new ArrayList(); 

    public Test() { 
     myList.add(new Object()); 
    } 

    public <T> Test(List<T> ttype) { 
     myList = ttype; 
    } 

    public <T> T returnWhat(int index) { 
     return (T) myList.get(index); 
    } 

    public <T> void addElement(T el) { 
     myList.add(el); 
    } 
} 

Второй не компилировать, когда MyList сделан общий. Как компилятор может определить тип <T> в том случае, когда используется конструктор по умолчанию?

Кроме того, это может привести к серьезным проблемам с объектами в коллекциях, которые полагаются на то, что вставлены только определенные типы.

Это будет генерировать следующее исключение:

Exception in thread "main" java.lang.ClassCastException: 
java.lang.Object cannot be cast to java.lang.String  at 
Test.main(Test.java:27) 

мне удалось убедить вас?

Реальный хороший вопрос, кстати. Мне пришлось подумать об этом совсем немного.

+0

Почему? если он работает над методами, а не в конструкторе? Я могу сделать то же самое с выводом метода, что-то вроде public T getType (T incoming) с Java 6 и использовать его. И для конструктора он говорит в Java Docs. Обратите внимание, что конструкторы могут быть общими (другими словами, объявлять собственные формальные параметры типа) как в родовых, так и в не общих классах. Рассмотрим следующий пример: класс MyClass { MyClass (T т) {// ... } } – Whimusical

+0

Спасибо теперь я действительно вижу, невероятный ответ. Еще раз спасибо! Главное, что я делаю в части addElement больше, чем другие, или, по крайней мере, тот, о котором я не думал. – Whimusical

0

No, Если вы хотите класс, который может содержать список параметризованного типа.

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

+0

Спасибо, но я хочу вернуться? где ? это то, что аррайалист носит так не конкретный тип (его конкретный, потому что арралист содержит только один тип внутри, но этот аррайалист может быть различным любым типом).Просто подумал, что в качестве последнего средства можно использовать частный var, чтобы держать его в качестве шаблона, но они уже сказали мне, что это невозможно по дизайну. – Whimusical

0

Что вы забыли, так это то, что не весь код, который вы можете запустить против, виден компилятору! Ящики могут быть добавлены, удалены, заменены во время выполнения, которые компилятор никогда не видел. Вы можете скомпилировать против интерфейс, который просто:

public interface MyClassFactory { 
    MyClass getInstance(); 
} 

Затем во время выполнения вы поставляете в JVM реализацию. Поэтому компилятор никогда не видел фактического кода, создающего MyClass, который вы будете использовать, поэтому нет способа выполнить такой вывод времени компиляции. Вы должны либо сделать класс обобщенным, либо принять, что безопасность типа не будет.

+0

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

+1

Дело в том, что это сделано во время компиляции. Если компилятор не видит вызов конструктора, но видит, что вы вызываете метод (что может легко произойти), тогда у него нет способа установить соединение. – Affe

1

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

public class MyClass<TYPE> { 
    private ArrayList<TYPE> incoming; 

    public MyClass(ArrayList<TYPE> incoming) { 
     this.incoming = incoming; 
    } 

    public TYPE getType() { 
     return incoming.get(0); 
    } 
} 

Таким образом, вы можете сделать:

ArrayList<Integer> numbers = createListOfNumbers(); 
MyClass<Integer> myClass = new MyClass<>(numbers); 
Integer number = myClass.getType(); 

Или я искажая вопрос, и вы хотите знать класс во время выполнения?

+0

Потому что мне нравится, как класс будет действовать как какая-то фабрика, и я думаю, и, возможно, я ошибаюсь, что компилятору не нужен тип класса для вывода его – Whimusical

+0

С помощью метода Id, который хотел бы использовать класс, мне нужно было бы вручную тип из ArrayList, чтобы передать его как тип для создания экземпляра класса. Представьте себе, что у меня нет информации «Integer» во второй строке, я получаю и ArrayList, и я должен искать контент для создания экземпляра моего класса, а затем после того, как он снова будет перенаправлен арраистом с типом, так зачем вам это делать дважды? – Whimusical

+1

Thats весь смысл безопасности типа, я думаю, что вам не хватает точки дженериков. Если вы не хотите вывести тип, просто не используйте generics, вы можете получить информацию о классе из объекта, возвращаемого MyClass.getType() – Terraego

2

Когда вы говорите, что вы хотите, чтобы компилятор «выводил входящий объект из конструктора в возвращаемый тип метода без предупреждений и кастингов и терял свойства», кажется, вы говорите, что он должен вывести результат от getType() от ввода конструктора. Если оба происходят в одной и той же функции, это возможно. Проблема заключается в том, что объект может не существовать только в одной функции, поэтому дополнительная информация типа (общий тип) необходима для передачи этого объекта между функциями.

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

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

+0

Извините, возможно, Im не правильно синтаксически. Мне нравится ваш ответ больше всего, это все, что я знаю, и все, что я хотел знать. Спасибо! – Whimusical

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