Если класс 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 ...