2012-06-19 1 views
9

Предположим, что у меня есть иерархию классов следующим образом:Java Generics, как применять два аргумента метода, которые расширяют суперкласс для того же типа?

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

я функцию, которая сравнивает два объекта

public <T extends Vehicle> generateDiff(T original, T copy) 

Во время компиляции, способ выше, гарантирует двух объектов Vehicle, но не может гарантировать, что типы двух объектов одинаковы.

generateDiff(new Car(), new Car()); //OK 
generateDiff(new Plane(), new Plane()); //OK 
generateDiff(new Car(), new Plane()); //WRONG 

Могу ли я получить это во время компиляции с помощью Generics?

P.s: В настоящее время я реализовал это, будет генерировать исключение, если Class двух объектов не совпадают. Но я не доволен этим.

Заранее спасибо.

ответ

8

Да, вы можете (вид)!

Типа T в настоящее время inferred из аргументов, но вы можете указать тип:

MyClass.<Car>generateDiff(new Car(), new Plane()); // generates a compile error 

без ввода метода, типа T выводится как самый узкий класс, который удовлетворяет оценки, используемые , так что для параметров Car и Plane, самый узкий тип, который будет работать в Vehicle, так что эти две строки эквивалентны:

generateDiff(new Car(), new Plane()); // type is inferred as Vehicle 
MyClass.<Vehicle>generateDiff(new Car(), new Plane()); 

Приведенный выше код предполагает, что generateDiff() является статическим методом. Если это метод экземпляра, вы можете ввести свой класс и использовать этот тип в своем методе.

+0

+1 для выводя тип! – UmNyobe

3

Невозможно, на мой взгляд, любой метод, который может принять Автомобиль в качестве аргумента, сможет принять CAR и PLANE. Поскольку компилятор не знает, какие объекты придут (может быть CAR, BUS, PLANE), он не может гарантировать, что два параметра имеют одинаковый тип. Если кто-то расширяет CAR и создает FORD? оба объекта имеют тип CAR.

Только способ обеспечить это во время выполнения с использованием пользовательской логики.

1

Нет, этого не может быть достигнуто с использованием дженериков или без них. По сути, вы спрашиваете, можете ли вы сообщить компилятору о нарушении правил полиморфизма.

Даже если вы явно определили метод без дженериков, как показано ниже, он все равно примет любую пару классов, которые расширяют Vehicle.

void generateDiff(Vehicle maybePlane, Vehicle maybeCar) { ... 

Существует один сценарий, который является исключением, но я бы не рекомендовал его. Если вы вызываете метод против объектов класса final (или любого класса, который не является расширенным), которые расширяют Vehicle, вы можете переопределить метод для соответствия этой подписи класса для каждого параметра. Но вам нужно определить каждый из них явно.

class Vehicle; 
final class Car extends Vehicle; 
final class Plane extends Vehicle; 

void generateDiff(Car car1, Car car2) { ... 
void generateDiff(Plane plane1, Plane plane2) { ... 

generateDiff(new Car(), new Car()); // OK 
generateDiff(new Plane(), new Plane()); // OK 
generateDiff(new Car(), new Plane()); // No matching method 
1

Могу ли я achive это во время компиляции с помощью Generics?

Неа, так как типы устанавливаются во время выполнения, но вы можете просто использовать самоанализ и выбросить исключение, если неверный тип передается в функцию, хотя, если тот случай, то они, возможно, Theres недостаток в дизайне , Но это только мое мнение.

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

public <T extends Vehicle> generateDiff(T original, T copy) 

, так как вы можете быть сравнение различных типов, может быть, вы не должны иметь одну функцию, так как для этого потребуется достаточное количество if else может быть разумнее реализовать функцию соответственно в каждом классе, чтобы они могли правильно сравнить с объектами соответствующего типа, хотя Im принимает справедливые допущения, которые могут быть неправильными.

6

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

public <C extends Vehicle> void generateDiff(Class<C> type, C original, C copy); 

И вы можете использовать его в качестве такового:

generateDiff(Plane.class, new Plane(), new Plane()); // OK 
generateDiff(Car.class, new Car(), new Car()); // OK 
generateDiff(Plane.class, new Plane(), new Car()); // ERROR 
generateDiff(Vehicle.class, new Plane(), new Car()); // OK 

Не уверен, почему любой здравомыслящий человек хотел бы сделать это, хотя! :)

+0

+1 для 'Не уверен, почему любой здравомыслящий человек захочет это сделать! :) ' –

0

Нет, что невозможно!

Это то, что я хотел бы сделать:

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

class DiffGenerator { 
    public Diff generateDiff(Car original, Car copy) { 
    return generateDiff(original, copy) 
    } 

    public Diff generateDiff(Plane original, Plane copy) { 
    return generateDiff(original, copy) 
    } 

    private Diff generateDiff(Vehicle original, Vehicle copy) { 
    return generatedDiff; 
    } 

} 

Заметили частный метод? private Diff generateDiff(Vehicle original, Vehicle copy)

3

Строго говоря, ответ на ваш вопрос «нет, это невозможно».

Однако, есть workaround. Создайте метод <T extends Vehicle> VehicleDiffer<T> compare(T vehicleA), где VehicleDiffer<T> имеет способ ReturnType with(T vehicleB). Теперь вы можете сделать следующие вызовы:

compare(new Car()).with(new Car()); // okay 
compare(new Plane()).with(new Plane()); // okay 

Следующая потерпит неудачу:

compare(new Car()).with(new Plane()); // with(Car) can't be called with argument type Plane 
+0

Идея гения, которую я говорю. – Saintali

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