Как написать метод в суперклассе, тип параметра которого ограничен другим объектом, принадлежащим к тому же подклассу вызывающего объекта

#java #oop #polymorphism #object-oriented-analysis

#java #ооп #полиморфизм #объектно-ориентированный анализ

Вопрос:

Я новичок в программировании на Java. Пожалуйста, рассмотрите следующий фрагмент кода.

 public class Animal {
    public void mate( /*what should I put here?*/ anotherAnimal ) {
    }
}

public class Cat extends Animal {
}

public class Dog extends Animal {
}
  

Я хочу написать метод Animal.mate() таким образом, чтобы при вызове метода из объекта подкласса аргумент, передаваемый методу, должен быть объектом того же подкласса, в противном случае возникает ошибка компилятора. Например:

 Cat cat = new Cat();
Dog dog = new Dog();
Animal randomAnimal = new Animal();
Cat trueLove = new Cat();

cat.mate( dog ); // raises a compiler error
cat.mate( randomAnimal ); //compiler error
cat.mate( trueLove ); //passes compiler check
  

Возможно ли то, о чем я спрашиваю? У меня есть смутное ощущение, что это можно было бы сделать с помощью дженериков.

Комментарии:

1. Вы могли бы использовать Animal<A extends Animal> , Cat extends Animal<Cat> и mate(A partner) .

2. Как насчет: Animal a = new Cat(); a.mate(new Animal()); Вы ожидаете, что это сработает или нет?

3. @Sweeper Было бы лучше, если бы вы могли дать мне что-то, что заставляет его работать, но я буду достаточно счастлив для чего-то, что только заставляет Cat cat = new Cat(); cat.mate( new Cat() ) работать

4. Так вы думаете, a.mate(new Animal()); следует скомпилировать? Я так не думаю. Однако во время выполнения a сохраняется Cat объект, а Cat объекты не могут сопрягаться с Animal объектами, верно?

Ответ №1:

Нет способа заставить компилятор предотвращать все неудачные вызовы. Даже если вы параметризуете Animal , все равно было бы возможно выполнить что-то вроде cat.mate(dog) во время выполнения.

Если этого требует ваша бизнес-логика, то лучше всего выполнить проверку с помощью чего-то подобного:

 public class Animal {
    public final void mate(Animal anotherAnimal ) {
        if(!this.getClass().equals(anotherAnimal.getClass())) {
            throw new IllegalArgumentException();
        }
    }
}
  

Если вы делаете это главным образом для улучшения проверки типов во время компиляции, то вы можете использовать дженерики, зная ограничения:

 class Animal<T extends Animal<T>> {
    public final void mate(T anotherAnimal) {

    }
}
  

Это сработает, если пользователи вашего API используют подклассы, а не Animal тип, и они не используют необработанные типы.

 Cat c = new Cat();
c.mate(new Cat());
c.mate(new Dog());//fails

Animal animal = c;      //raw type
animal.mate(new Dog()); //only a warning
  

Ответ №2:

ваша проблема связана с полиморфизмом. используйте суперкласс в качестве типа параметра в методе mate.суперкласс — это Animal. это код:

 public class Animal {
public void mate(Animal animal) {
    System.out.println("Animals mating");
}
@Override
public String toString() {
    return "Animal";
}

public class Dog extends Animal {
@Override
public String toString() {
    return "Dog";
}
public class Cat extends Animal {
@Override
public void mate(Animal obj) {
    System.out.println("cat mating with "   obj );
}

@Override
public String toString() {
    return "cat";
}}
  

и запустите свой код в основном методе. ошибки исчезли, и это ответ:

 cat mating with Dog.
cat mating with Animal.
cat mating with cat.
  

для лучшего ответа вместо того, чтобы говорить cat или dog , вы можете определить поле name в классе Animal.