Как поменять местами два элемента в generics
Это копия удалённого вопроса, который показался мне интересным.
public class Swap<A,B> { private A first; private B second; public Swap(A first, B second) { this.second = second; this.first = first; } void setFirst (A first) { this.first = first; } void setSecond(B second) { this.second = second; } A getFirst() { return first; } B getSecond() { return second;} void swap(Pair[]pairs,A first,B second) { Pair temp=pairs[first]; pairs[first]=pairs[second]; pairs[second]=temp; } }Я немного запутался с реализацией swap метода, помогите его исправить пожалуйста.
Ответы (1 шт):
Если класс Swap объявлен как обобщённый, то в его экземпляре нельзя переставить объекты произвольных типов A и B.
Однако можно создать новый типизированный экземпляр класса Swap и изменить в нём порядок аргументов:
public class Swap<A, B> {
private A first;
private B second;
public Swap(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst() {return first;}
public B getSecond() {return second;}
@Override
public String toString() {
return "Swap: first(" + first.getClass() + ")=" + first + "; second(" + second.getClass() + ")=" + second;
}
// статический метод
public static<A, B> Swap<B, A> swap(Swap<A, B> item) {
return new Swap<>(item.second, item.first);
}
// метод экземпляра
public Swap<B, A> swap() {
return new Swap<>(this.second, this.first);
}
}
Тест:
Swap<Integer, String> foo = new Swap<>(1, "foo");
var bar = Swap.swap(foo);
var baz = bar.swap();
System.out.println("foo: " + foo);
System.out.println("bar: " + bar);
System.out.println("baz: " + baz);
foo: Swap: first(class java.lang.Integer)=1; second(class java.lang.String)=foo
bar: Swap: first(class java.lang.String)=foo; second(class java.lang.Integer)=1
baz: Swap: first(class java.lang.Integer)=1; second(class java.lang.String)=foo
Классическая корректная перестановка допустима только если оба параметра first и second имеют один общий тип A:
class Pair<A> {
private A first;
private A second;
// ... конструкторы / геттеры / сеттеры и т.п.
public void swap() {
A tmp = first;
first = second;
second = tmp;
}
}
Разумеется, можно написать реализацию swap для "сырого" (нетипизированного) типа (см. недавний похожий вопрос как в List<Integer> добавить объект типа String?), однако, пользоваться таким классом будет затруднительно:
// class Swap
// код рабочий
// предупреждение: Raw use of parameterized class 'Swap'
public static void rawSwap(Swap swap) {
Object a = swap.first;
swap.first = swap.second;
swap.second = a;
}
Например, "переставим" поля в экземпляре baz:
Integer f1 = baz.getFirst();
String s1 = baz.getSecond();
System.out.println("f1: " + f1 + "; s1: " + s1);
Swap.rawSwap(baz); // "сырая" перестановка
Теперь после такого обмена вся информация о типах объектов в экземпляре swap будет потеряна, и например пользоваться геттерами/сеттерами будет невозможно:
- если обратиться к геттеру по "новому" типу, возникнет ошибка компиляции о несовместимости типов -- компилятор "не в курсе", что тип уже изменился:
// Integer ff = baz.getSecond(); // java: incompatible types: java.lang.String cannot be converted to java.lang.Integer
// String ss = baz.getFirst(); // java: incompatible types: java.lang.Integer cannot be converted to java.lang.String
- обращение по "старому" типу приведёт к
ClassCastException:
System.out.println("swp: " + baz);
Integer f2 = baz.getFirst(); // java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
String s2 = baz.getSecond();
System.out.println("f2: " + f2 + "; s2: " + s2);
Результат:
f1: 1; s1: foo
swp: Swap: first(class java.lang.String)=foo; second(class java.lang.Integer)=1
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer ...