2010-12-29 2 views
2

Я пытаюсь использовать общий метод в первый раз и немного смущен. Я создал простой пример, чтобы продемонстрировать, что я, вероятно, ошибаюсь в этом и нуждаюсь в исправлении. Я использую Eclipse 3.6.1. У меня создалось впечатление, что компилятор определил типы аргументов с помощью вывода, но я не уверен, почему он заставляет меня использовать кастинг в универсальном методе. Это простой пример.Методы Java Generics - использование в первый раз

class Test1 
{ 
    Test1() {}; 
    public String getX() {return "Test1"}; 
}; 

class Test2 
{ 
    Test2() {}; 
    public String getX() {return "Test2"}; 
}; 

мой основной метод:

public static void main(String args[]) 
{ 
    Test1 tst1 = new Test1(); 
    Test2 tst2 = new Test2(); 

    System.out.println("result: " + displayTest(tst1, tst2)); 
} 

static <T,S> boolean displayTest(T x, S y) 
{ 
    System.out.println("X: " + ((Test1) x).getX()); 
    System.out.println("Y: " + ((Test2) y).getX()); 

    if (((Test1) x).getX().equals(((Test2) y).getX())) 
     return true; 
    else 
     return false; 
} 

Я думал, что компилятор будет знать, что T в данном случае представляет собой пример Test1 и S был Test2, но в Eclipse, getX не является правильным методом. Чтобы заставить это скомпилировать, это заставляет меня подвергать объекты правильному типу, который, как мне кажется, противоречит общим принципам общего метода.

Очевидно, что я не получаю это и делаю что-то неправильно. Как компилятор знает, какие типы в общем методе тогда? Как это должно быть сделано? В моей большой системе, где я пытаюсь реализовать это, у меня есть несколько методов, которые работают с различными типами объектов и пытаюсь сделать их обобщенными. то есть метод 1 вызывает метод 2 (который использует общий тип), который, в свою очередь, вызывает метод 3 (снова передавая общие типы). Я надеялся, что только начало вызовов функций (вызов метода 1 в этом случае) необходимо знать, какой тип объектов был, а все последующие методы - только общие методы.

Большое спасибо.

ответ

0

Зачем компилятору известно, что T и S являются Test1 и Test2? Вы можете вызывать displayTest из любого места вашего кода, сколько угодно раз с любым количеством разных типов объектов. Что он должен там делать?

Кроме того, eclipse! = Компилятор. Завершение кода Eclipse не «компилирует» что-либо. Компилятор точно знает, какие типы вводятся там, но только для каждого вызова этой функции. Вот почему он называется родовым. Он может принимать любой общий тип.

3

Компилятор не знает типы внутри метода. Они могут быть чем угодно.

Если вы используете <T extends Y>, где Y - это интерфейс, определяющий getY(), это сработает.

Пункт дженериков заключается в обеспечении безопасности во время компиляции. В основном это понятие времени компиляции. Например:

public static <T> T instantiate(Class<T> clazz) throws Exception { 
    return clazz.newInstance(); 
} 

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

Foo foo = instantiate(Foo.class); 
Bar bar = instantiate(Bar.class); 

Другой пример, из рамок коллекций. Существует Collections.enumeration(collection), который является общим. Итак:

Enumeration<String> enumeration1 = 
     Collections.enumeration(new ArrayList<String>(..)); 
Enumeration<Integer> enumeration2 = 
     Collections.enumeration(new ArrayList<Integer>(..)); 

Никаких бросков, но вы уверены, что это будут типы.И тогда метод nextElement() будет возвращать либо String или Integer, без необходимости отливать:

String s = enumeration1.nextElement(); 
Integer i = enumeration2.nextElement(); 

Без дженериков, вам нужно будет использовать забросы в этих примерах, и если вы прошли неправильный аргумент, вы получите исключение во время выполнения (скорее всего, ClassCastException). С generics вы получаете исключение во время компиляции.

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

+0

+1 Бей меня к нему. – Poindexter

1

Проблема в том, что во время компиляции компилятор не знает конкретного типа T и S. Ваш основной метод вызывает displayTest с конкретными экземплярами Test1 и Test2, но нет ничего, что помешает вам позвонить ему, скажем, String и Integer.

Это означает, что вы не можете назвать getX на x, потому что вы не можете гарантировать, что T подкласс Test1.

Вы можете ограничить тип T и S с захватами:

boolean <T extends Test1, S extends Test2> displayTest(T x, S y) 

Это говорит компилятору, что T должен стать Test1 (или подкласс Test1), который означает, что вам не нужно бросать.

+0

А, Ок. Я получаю это сейчас. Как я уже сказал, впервые использовал дженерики. Спасибо за все быстрые ответы .... – Marc

0

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

Поскольку displayMethod является универсальным методом, «правильный» способ вызвать метод, чтобы сказать компилятору, что T и S стенд для в данном вызове метода путем вызова его в качестве

ClassName.<Test1,Test2>displayMethod(tst1,tst2)

Но поскольку Java умный, компилятор может вывести ж/о ты говоришь это именно то, что T и S, что T и S будут Test1 и Test2, если вы просто делаете,

ClassName.displayMethod(tst1, tst2)

Кроме того, как Cameron Skinner сказал, что вы можете исправить свой код, заставив T расширять Test1 и S расширять Test2. Другое исправление, которое вы можете сделать, это оставить <T, S> как есть, и переименовать ваш метод getX() для переопределения метода toString(). Поскольку toString() является членом Object класса, общие классы T and S будут иметь доступ к нему, и вы не должны ограничивать свой метод только принимать аргументы, которые типа Test1 and Test2

+0

Я пробовал это во-первых, но не мог работать. – Marc

+0

это не сработало, потому что T и S не имеют доступа к getX(). Это совершенно другая проблема. INFERENCE позволяет компилятору вывести тип классов в вызове метода. Но общие типы T и S должны иметь доступ ко всему методу, который вы вызываете на T и S.Так как T и S являются типичными типами и, следовательно, для типа Object, у них нет доступа к getX() –

+0

Да, конечно, не думал об этом. В моем случае, однако, мне нужен доступ к нескольким методам в объектах, поэтому перегрузка toString() не будет работать для этого случая. Тем не менее, я просто понял, что мне нужно ограничить как T, так и S, но в этом случае мне нужно, чтобы каждый из них мог быть Test1 или Test2. Как я могу это сделать? – Marc

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