Могут ли два обобщенных метода при одном вызове вызвать ошибку компиляции при попытке их использования? - PullRequest
3 голосов
/ 21 октября 2019

Я прочитал книгу Дейтала: «Как программировать на Java», а в Общей главе написано предложение: «Если компилятор не находит объявление метода, которое точно соответствует вызову метода, но находит два или болееметоды, которые могут удовлетворить вызов метода, возникает ошибка компиляции ". Может кто-нибудь дать мне пример ситуации, потому что, когда мне не удалось получить вышеуказанную ошибку компиляции. мой код:

public class Animal
{
    public Animal ()
    {

    }

    public String bark()
    {
        return "Animal";
    }
}

public class Dog extends Animal  
{
    public String bark()
    {
        return "howhow";
    }
}

public class DogSon extends Dog
{
    public String bark()
    {
        return "I'm  Dog Son";
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Test t = new Test();
        DogSon d = new DogSon();
        System.out.println(t.helpMethod(d));
    }

    public <T extends Dog> String helpMethod(T dog)
    {
        System.out.println("aaa");
        return dog.bark();

    }
    public <T extends Animal> String  helpMethod(T animal)
    {
        return animal.bark();
    }
}   

Так как не существует метода, который бы точно получал объект son, и есть два универсальных метода, которые могут подойти здесь. Разве это не та ситуация, о которой говорит Дейтал?

Ответы [ 3 ]

2 голосов
/ 21 октября 2019

Причина, по которой вы не получаете ошибку компиляции в вашем случае, заключается в том, что оба метода

public <T extends Dog> String helpMethod(T t) {

    return t.bark();
 }
public <T extends Animal> String helpMethod(T t) {

   return t.bark();
}

сообщают компилятору разрешить T в Dog & Animal соответственно,Что на самом деле аналогично написанию

public String helpMethod(Dog t) {

    return t.bark();
}
public String helpMethod(Animal t) {

    return t.bark();
}

Поскольку Dog & Animal - это два разных типа, несмотря на то, что они находятся в отношениях родитель-ребенок. Компилятор не будет жаловаться.

Однако, если вы ищете ошибку компиляции, попробуйте это

public <T extends Dog> String helpMethod(T t) {

    return t.bark();
}
public <T extends Dog> String helpMethod(T t) {

    return t.bark();
}
2 голосов
/ 21 октября 2019

Не совсем;в вашем примере вариант T dog более конкретен, чем вариант T animal, и, следовательно, он выбран.

Я не могу гарантировать, что знаю, на что ссылается автор. Но я могу догадаться:

public class Example<T> {
    public void m(T arg) {
        // At compile time, 'T', as it has a lower bound of Object,
        // is treated as Object. Its signature therefore does not clash
        // with the next m, and thus this can be compiled.
        System.out.println("T-based");
    }

    public void m(Dog arg) {
        System.out.println("Dog-based");
    }
}

new Example<Dog>().m(new Dog()); // this line causes a compiler error.

Сгенерированная ошибка:

Animal.java:16: error: reference to m is ambiguous
    new Example<Dog>().m(d);
                      ^
both method m(T) in Example and method m(Dog) in Example match
where T is a type-variable:
    T extends Object declared in class Example
1 error

Итак, что здесь происходит: во время компиляции (самого Example.java) два m методы еще не всегда неоднозначны;Т-вариант рассматривается для большинства целей так, как если бы он был равен его нижней границе (то есть Object), и в Java вы можете иметь m(Object arg) и m(Dog arg) в одном классе, без проблем.

Однако после обобщенных методов методы 2 m действительно ДЕЙСТВИТЕЛЬНО конфликтуют: теперь они оба принимают в качестве аргумента что-либо типа Dog. Таким образом, больше нельзя вызывать m-метод для любого new Example<Dog>. Правильное решение - не перегружать (это то, что называется, когда у вас есть 2 разных метода с одинаковыми именами), когда используются дженерики и параметры типа могут перекрываться. Предположительно, автор этого раздела вашей книги пытается сказать вам, ПОЧЕМУ вы никогда не должны перегружаться в таком случае.

0 голосов
/ 21 октября 2019

То, что вы должны понимать, называется Стирание универсальных методов

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

Связанный означает, что, оставаясь на вашем примере, вы объявили отношениепараметр типа T для другого класса, как вы это сделали:

// 1. method  
public <T extends Dog> String helpMethod(T dog)
// 2. method
public <T extends Animal> String  helpMethod(T animal)

T равен ограничен Dog в методе 1., и привязан кЖивотное в 2. метод.

Итак, когда дело доходит до компиляции, Java заменяет T на Dog в методе 1. и на Animal в методе 2.
Здесь нет двусмысленности,это два разных метода.

Однако, если вы объявите 3. метод:

// 3. method
public <T> String helpMethod(T dog)

Тогда T будет несвязанным , вы не объявилисвязь между T и другим классом, поэтому, когда дело доходит до компиляции, Java изменяет T на Object .

Теперь, если вы попытаетесь объявить 4. метод:

// 4. method
public  String helpMethod(Object dog)

Будет ошибка компиляции , как метод 3. во время стирания типаиметь точно такую ​​же сигнатуру метода как ваш 4. метод.
Компилятор не может решить, какой метод вы хотите вызвать, и выдает ошибку.

Учитывая вышесказанное, короткий ответ :

В вашем коде вы используете два разных метода, нет двусмысленности, поэтому ошибки не возникает.

Если вы хотите увидеть эту ошибку компиляции, вы должны объявить другой универсальный метод, сигнатура которого после компиляции будет такой же, как существующий универсальный / неуниверсальный метод в вашем классе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...