Параметр Java generics, ограниченный любым диапазоном типов

Есть ли синтаксис или обходной путь для ограничения параметра типового типа для любого из нескольких типов?

Я знаю, что вы можете ограничить тип всем диапазоном типов (т.е. AND logic):

public class MyClass<T extends Comparable<T> & Serializable> { } // legal syntax

Существует ли логическая версия OR, например:

public class MyClass<T extends Comparable<T> | Serializable> { } // illegal syntax

Если нет синтаксиса, который поддерживает это (я не думаю, что есть), есть ли способ обхода или подход, который является хорошим шаблоном?

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

/** @return true if the obj is either has the same id, or if obj is the same as id */
public <T extends MyClass | String> boolean sameAs(T obj) {
    if (obj instanceof String) return this.id.equals(obj);
    if (obj instanceof MyClass) return this.id.equals(((MyClass)obj).id);
    return false;
}

Люди, похоже, повесились на точной семантике моего метода, описанного выше. Попробуйте это вместо этого:

public class MyWrapper<T extends A | B> {
    // my class can wrap A or B (unrelated classes). Yes I will probably use instanceof
}

Редакция:
Я не буду знать во время компиляции, которое я мог бы получить (исходя из внешнего кода), поэтому я хочу избежать создания конкретных классов для каждого типа. Кроме того, я должен отдать свой класс иностранной системе, которая вызывает мой класс .method, но другая система может предоставить мне экземпляры различных классов, но узко определенную и известную разновидность.

Некоторые люди прокомментировали, что instanceof является "нечистым". Ну, одним из способов является использование метода factory для выбора моего конкретного класса на основе класса входящего объекта, но этот метод factory должен был бы использовать instanceof, поэтому вы просто перемещаете instanceof в другое место - вам все равно нужен instanceof.

Или эта идея просто не всегда хороша?

Ответ 1

Нет. Это не имело бы смысла, если бы все типы не имели пустой тип объединения, например. интерфейс, который они все реализовали, или базовый класс, который они все расширили, и в этом случае вы просто указываете тип объединения.

Ответ 2

public class MyWrapper<T extends A | B> {}

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

interface AOrB {
}

interface A extends AOrB {
    someMethodHere();
}

interface B extends AOrB {
    someOtherMethodHere();
}

public class MyClass<T extends AOrB> {}

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

Ответ 3

В то время как Java имеет ограниченную поддержку " типы пересечений" как T1 & T2, поддержка "типов объединения" скудна.

Общие типы с подстановочными символами являются фактически типами объединения: G<? extends X> является объединением всех G<S>, где S является подтипом X.

Нет поддержки объединения произвольных двух типов.

Синтаксис с несколькими лотками Java 7 выглядит так, будто он поддерживает объединение произвольных типов исключений

catch (IOException|SQLException ex){ .. }

но на самом деле тип ex является фиксированным суперклассом, а не объединением двух классов.

Ответ 4

Использование instanceof считается не очень хорошим стилем программирования и позволяет использовать OR в дженериках, что вы будете использовать его.

Ответ 5

Следующий код будет делать то же самое, что и в приведенном примере, но без проверки типа исполнения и типов.

public boolean sameAs(MyClass obj) {
    return this.id.equals(obj.id);
}
public boolean sameAs(String obj) {
    return this.id.equals(obj);
}

Проверка NPE может быть хорошей идеей.