Неопределенный класс в аргументах метода у интерфейса

public interface ClassWithList {
    public void clearList();
    public void tryAdd(Class<T> b);
    public void update(Class<T> c);
}

При расширении указываю конкретный класс, и возникает ошибка.

public class Test implements ClassWithList {
    @Override
    public void clearList() {}
    
    @Override
    public void tryAdd(Test b) {
        // Error
    }
    
    @Override
    public void update(Test c) {
        // Error
    }
}

Может ли Java расширять интерфейс подобным образом?
Подскажите, что я делаю не так. Спасибо!


Ответы (1 шт):

Автор решения: Nowhere Man

Поскольку в представленном коде возникают ошибки компиляции, связанные с неопределённым типом T в интерфейсе ClassWithList, прежде всего нужно решить эту проблему.

cannot find symbol  
public void tryAdd(Class<T> b);
                         ^
symbol:   class T
location: interface ClassWithList

error: cannot find symbol
public void update(Class<T> c);
                         ^

Для этого существуют два способа:

  1. Объявить интерфейс ClassWithList обобщённым
    В таком случае аргументы у методов tryAdd, update будут использовать один и тот же тип.
    При этом, в методах tryAdd, update аргумент должен иметь тип T, а не Class<T>, если в классе Test, реализующем данный интерфейс, аргумент в упомянутых методах должен иметь тип Test:
public interface ClassWithList<T> {
    public void clearList();
    public void tryAdd(T b);
    public void update(T c);
}

Тогда при объявлении класса Test следует указать в угловых скобках тип класса-параметра:

public class Test implements ClassWithList<Test> {
    @Override public void clearList() {}
    @Override public void tryAdd(Test b) {}
    @Override public void update(Test c) {}
}

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

Test t = new Test();
t.tryAdd(t);

1.а) Если аргументы в упомянутых методах по какой-либо причине должны сохранить тип Class<T>, потребуется следует изменить реализацию, чтобы она соответствовала интерфейсу.

public interface ClassWithListA<T> {
    public void clearList();
    public void tryAdd(Class<T> b);
    public void update(Class<T> c);
}

public class TestA implements ClassWithListA<TestA> {
    @Override public void clearList() {}
    @Override public void tryAdd(Class<TestA> b) {}
    @Override public void update(Class<TestA> c) {}
}

При вызове методов в данной реализации можно передавать только тип, известный на момент компиляции, т.е. TestA.class:

ClassWithListA t2 = new TestA();
// ок
t2.update(TestA.class);
// ошибка
t2.update(t2.getClass()); 
// Class cannot be converted to Class
// where CAP#1 is a fresh type-variable:
//   CAP#1 extends TestA from capture of ? extends TestA

  1. Объявить каждый из методов tryAdd, update как обобщённый
public interface ClassWithListB {
    public void clearList();
    public <T> void tryAdd(Class<T> b);
    public <T> void update(Class<T> c);
}

Поскольку в объявлении этого интерфейса не указан параметр типа, он сам по себе не обобщённый, поэтому в реализации также останутся обобщённые методы:

class TestB implements ClassWithListB {
    @Override public void clearList() {}
    @Override public <T> void tryAdd(Class<T> b) {}
    @Override public <T> void update(Class<T> c) {}
}

То есть, в них можно будет передавать объекты любых классов:

ClassWithListB t3 = new TestB();

t3.update(ClassWithListA.class);
t3.tryAdd("abcd".getClass());
t3.update(t2.getClass());
t3.tryAdd(t3.getClass());

Вопрос о необходимости такой реализации остается открытым.

→ Ссылка