2011-01-24 9 views
5

Chcę kodu pojedynczej metody add() w Javie, które mogłyby dodać zarówno liczby całkowite, ciągi itp. Czy generics pomoże mi.Dlaczego generics w Javie? Czym różni się od przeciążania?

Nie mogłem zrozumieć ostatecznego celu Generics. Jestem taki skołowany.

Generics vs Overloading?

public Integer add(Integer i, Integer j){return i+j;} 
public String add(String i, String j){return i+j;} 
public <T> T add(T i, T j){return i+j;} //this gives me error. 

Proszę, wyciągnij mnie z tego.

Dzięki.

+1

Kiedy wykonujesz 'Integer i + Integer j', tak naprawdę nie dodajesz' Integers'. 'Liczba całkowita' nie obsługuje operatora' + '. Zostaną one rozpakowane do 'ints', a nie bezpośrednio dodane jako obiekty" Integer ". Nie można dodawać obiektów ogólnych, ponieważ obiekty nie obsługują operatora '+'. 'Struny' są szczególnym przypadkiem i wspierają' + '. –

+4

Jedynym powodem, dla którego nazwiesz oba przykłady 'add', jest użycie operatorów' + ', co w rzeczywistości jest bezsensownym podobieństwem, ponieważ ma zupełnie inne znaczenie w przypadku łańcuchów i liczb całkowitych. Metody nazw oparte na tym, co faktycznie robią z punktu widzenia wywołującego - tj. "Suma" i "konkatenacja" w tym przypadku byłyby dobrymi nazwami. –

Odpowiedz

4

Chcę kodu pojedynczej metody add() w Javie, które mogłyby dodać zarówno liczby całkowite, ciągi itp. Czy generics pomoże mi.

NO

Leki generyczne są wykorzystywane do Typ bezpieczeństwa głównie w czasie kompilacji.

Można podać tylko jeden typ na dodatek w kolekcji

List<String> lst;

zaakceptuje String tylko nie nadklasą String ma klasy sub [na przykład, jeśli istnieje]

+1

'zaakceptuje tylko String, nie będzie nadklasy String no sub class' - To nie jest prawda. Coś jak "void add42" (lista ) {list.add (42);} 'jest całkowicie poprawne. – maaartinus

5

Generics może pomóc, Problem polega na tym, że operacja + jest zdefiniowana tylko dla prymitywów java i String, ale nie dla typów w ogóle. A w Javie nie możemy przeciążać operatorów (jak na przykład w C++).

Praktycznym rozwiązaniem byłoby bez rodzajowych:

public Integer add(Integer a, Integer b) { return a + b; } // sum of a and b 
public String add(String a, String b) { return a + b; }  // concatenate operation! 
public MyType add(MyType a, MyType b) { return a.add(b); } // requires add operation 
+2

Twój drugi akapit nie jest prawdziwy. Zwracany typ nie jest częścią sygnatury metody w Javie, tylko nazwy i typy parametrów. Dopóki parametry są różne, możesz mieć różne typy zwracania. A może źle zrozumiałem, co napisałeś? –

+0

Przeciążenie nie jest określane za pomocą typu zwrotu. Nie jest konieczne, aby typ powrotu był taki sam dla przeciążenia. – Manoj

+0

@ Jonathan - wielkie dzięki - to był typowy ShortOnCoffeeError :-) Zmieniono odpowiedź na zarezerwowany kolejny kurs "Java dla początkujących";) –

2

Leki generyczne są miłe, ponieważ jeśli prawidłowo pozwala uogólniać niektóre kodu masz napisane. Jeśli chodzi o pisanie oprogramowania, chcesz zminimalizować niepotrzebny kod i ponownie użyć jak najwięcej kodu.

Stosując metodę przeciążania, należy wypisać każdy z przeciążeń, natomiast w przypadku leków generycznych wystarczy zrobić to tylko raz. Generics działa dobrze w sytuacjach, w których manipulujesz kolekcją, ponieważ wykonujesz pewne typowe operacje na zbiorze zbiorów. W niektórych sytuacjach, jeśli pragniesz bardziej spersonalizowanego podejścia do radzenia sobie z niektórymi sytuacjami, być może będziesz musiał przeciążać metody, ale chciałbym, aby pisanie generycznych było celem, jeśli możesz używać go w wielu miejscach.

+0

Więc pomyliłem się z Java Generics z szablonami C++. Jeśli jakikolwiek link do porównywania Generics i szablonów jest dla mnie bardziej przydatny. – Manoj

+1

http://stackoverflow.com/questions/36347/what-are-the-differences-between-generic- types-in-c-and-java Może pomóc ci zrozumieć podobieństwa i różnice między tymi dwoma. –

1

To jest dokładnie kwestia generycznych. Nie chcesz wdrożyć jednej metody, która może przyjąć wszystko. Szczerze mówiąc zawsze można wdrożyć metodę

add(Object obj)

i przekazać go do liczby całkowite, ciągi znaków, wartości logiczne ... ale w większości przypadków nie chcą się do tego.Metoda ma do czynienia z argumentem i dlatego powinna znać typ parametru. To jest powód, że jeśli chcesz, aby metodę Add, który odbiera ciągi i całkowite wdrożenie 2 metod:

add(String s); 
add(int i); 

Teraz nie można wysłać logiczną dodać() metoda: taka metoda po prostu nie istnieje.

można również realizować metoda rodzajowa

<T> void add(T arg);

i nazywają go: this.<String>add("hello"); lub this.<Integer>add(123);

W tym przypadku this.<String>add(123); powoduje błąd kompilacji.

3
public Integer add(Integer i, Integer j){return i+j;} 
public String add(String i, String j){return i+j;} 
public <T> T add(T i, T j){return i+j;} //this gives me error. 

Mimo że metody wyglądają niemal identycznie, w tym przypadku generics nie może pomóc. Rozważmy to:

public <T> T add(T i, T j){return i+j;} 
... 
add(new Object(), new Object()); 

Co to znaczy dodać do zwykłych Object s razem? Nie, + nie jest obsługiwany dla Object i nie jest obsługiwany dla wielu innych typów, które można zastąpić T.

Tworząc metoda rodzajowa <T> T add(T i, T j) metoda rodzajowa ogranicza się do metod i działań, które są dozwolone na uniwersalnym typem parametru T, który skutecznie oznacza jedynie rzeczy, które można zrobić z Object. Próbujesz użyć metody/operacji +, która jest coś, co możesz zrobić z Object.

Możemy spróbować rozwiązać ten problem, znajdując wspólną klasę bazową String i Integer, która obsługuje wszystkie metody i operacje, których musimy użyć w naszym ogólnym obiekcie metody. Jeśli istniały takie wspólne klasa bazowa XXXXX że obsługiwane operatora +, możemy to zrobić:

public <T extends XXXXX> T add(T i, T j) { return i+j; } 

Jednak istnieje żadna klasa wspólna podstawa XXXXX który obsługuje wszystko, co chcemy zrobić w metoda ogólna (+), więc generycznych nie można użyć do rozwiązania tego problemu.

+1

Dzięki za wzmiankę o 'extends', chciałem to zrobić samemu. –

1

Jak o podejściu z rodzajowego interfejsu:

interface Adder<T> { 
    T add(T first, T second); 
} 

I klasy Manager:

public class AdderManager{ 

    public <T> AdderManager putAdder(final Class<T> clazz, 
     final Adder<? extends T> adder){ 
     adders.put(clazz, adder); 
     return this; 
    } 

    @SuppressWarnings("unchecked") 
    public <T> Adder<T> getAdder(final Class<T> clazz){ 
     return (Adder<T>) adders.get(clazz); 
    } 

    private final Map<Class<?>, Adder<?>> adders = 
     new HashMap<Class<?>, Adder<?>>(); 

} 

Teraz można zarejestrować i używać własnych adders dla różnych klas:

final AdderManager manager = new AdderManager(); 
manager 
.putAdder(String.class, new Adder<String>(){ 

    @Override 
    public String add(final String first, final String second){ 
     return first.concat(second); 
    } 
}) 
.putAdder(Integer.class, new Adder<Integer>(){ 

    @Override 
    public Integer add(final Integer first, final Integer second){ 
     return first + second; 
    } 
}) 
.putAdder(List.class, new Adder<List<?>>(){ 

    @Override 
    public List<?> add(final List<?> first, final List<?> second){ 
     final List<Object> newList = new ArrayList<Object>(); 

     return newList; 
     } 
}); 

A teraz możesz użyć tych dodatków takich jak ten:

String addedString = manager.getAdder(String.class).add("abc", "def"); 

@SuppressWarnings("unchecked") // this is necessary because of 
           // the generic <Integer> type 
List<Integer> addedList = manager 
          .getAdder(List.class) 
          .add(
           Arrays.asList(1,2,3), 
           Arrays.asList(4,5,6) 
          ); 
1

Generics w java dodaje tylko rzutowanie do kodu bajtowego.

Kod będzie taki sam, z wyjątkiem odlewania.

tak więc dwie konstrukcje:

List list1 = new ArrayList(); 
List<String list2 = new ArrayList<String>(); 
.... 
String string1 = (String) list1.get(0); 
String string2 = list2.get(0); 

robią to samo.

5

Inni zwrócili uwagę, dlaczego to nie działa. Pozwól mi tylko dodać, myślę, że nie rozumiesz do czego służą generyczne Java. Być może znasz już szablony C++ lub makro C, które przypominają generyczne, ale są czymś zupełnie innym.

Generics są tak naprawdę związane z bezpieczeństwem typu i unikaniem niechlujnych rzutów. Mówisz kompilatorowi, że pewna kolekcja zawiera ciągi znaków, a następnie wie, że wszystko, co umieścisz lub wyciągniesz, musi być łańcuchem. Dostajesz trochę czasu na kompilację rzeczy, które wkładasz, i zapisujesz kilka rzutów na rzeczy, które wyciągasz.

NIE powoduje kompilacji generowania różnych kodów w zależności od typu. Generics nie są łatwym sposobem na uniknięcie konieczności pisania, powiedzmy, zarówno wersji "int", jak i "float" tej samej funkcji, jak na przykład makra lub szablon C++. Być może będziesz w stanie uzyskać takie pożądane, odrębne zachowanie, mając dwie klasy, które realizują tę samą funkcję, ale jedną używając int, a drugą używającą float. Ale można to zrobić za pomocą technik zorientowanych obiektowo bez generycznych. Jeśli nie można uzyskać pożądanego wyraźnego zachowania przy użyciu "nie ogólnych" technik zorientowanych obiektowo, to nie można tego zrobić za pomocą leków generycznych.

Powiązane problemy