Как я могу выбросить ClassCastException из универсальной оболочки или во время компиляции - PullRequest
0 голосов
/ 04 марта 2019

Эта проблема возникает конкретно в Android (но не ограничивается).Таким образом, мы создаем 2 оболочки, но назначение объекта выполняется внутри (если бы я создал оболочку wrongWrapper, используя new AWrapper<>((B1) findObjectStupid(false));, это дало бы ClassCastException сразу, а не позже)

class A {}
class B1 extends A {}
class B2 extends A {}

class AWrapper<T extends A> {
    T a;

    AWrapper(T a) {
        this.a = a;
    }

    AWrapper(boolean b) {
        this((T) findObjectStupid(b));  //this throws a ClassCastException only if findObjectStupid(b) is not an A class
    }

    public T getA() {
        return a;
    }
}

<T extends A> T findObjectStupid(boolean b) { /in android this method mimics the findViewById(int id) 
    if (b) {
    return (T) new B1();
    } else {
    return (T) new B2();
    }
}

void main() {
    AWrapper<B1> rightWrapper = new AWrapper<>(true);
    AWrapper<?> dontCareWrapper = new AWrapper<>(true); 
    AWrapper<B1> wrongWrapper = new AWrapper<>(false); //should give ClassCastException now, but doesn't
    //AWrapper<B1> wrongWrapper = new AWrapper<>((B1) findObjectStupid(false)); //this way ClassCastException is thrown immediately

    B1 rightObject = rightWrapper.getA();
    A dontCareObject = dontCareWrapper.getA(); // we know we can cast it to B1 and it would work
    B1 wrongObject = wrongWrapper.getA(); //gives ClassCastException now, not good!
}

Очевидно, чтоОболочка разработана, чтобы иметь методы, которые используют Объект как A, единственный тип времени, в котором действительно необходим тип T, - это когда вызывается getA(), что делает эту обертку проблематичной, если рассматриваемый программист забывает сегодня вызвать этот конкретный метод, и ошибка может остатьсяскрытый в течение долгого времени, пока он действительно не понадобится.

Как можно изменить вышеупомянутую оболочку, чтобы она создавала исключение ClassCastException при создании, а не при вызове getA()?

1 Ответ

0 голосов
/ 04 марта 2019

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

class AWrapper<T extends A> {
    T a;
    Class<? super T> tClass; //new
    //Class<T> tClass; //or like this if exact match is needed

    AWrapper(T a, Class<? super T> tClass) { //edited
    //AWrapper(T a, Class<T> tClass) { //or like this if exact match is needed
        if (a.getClass().isInstance(tClass)) { //new
        //if (a.getClass().equals(tClass)){ //or like this if exact match is needed
            throw new ClassCastException("Object being wrapped is not of good type, the cause and solution is simply a programmer declared this generic type of an uncompatible type, this exception is designed to reduce runtime ClassCastExceptions by throwing them not at compiletime(impossible) but as soon as possible"); //new
        }
        this.a = a;
        this.tClass = tClass; //new
    }

    AWrapper(boolean b, Class<T> tClass) { //edited
        this((T) findObjectStupid(b), tClass); //edited
    }

    public T getA() {
        return a;
    }
}

<T extends A> T findObjectStupid(boolean b) {
    if (b) {
        return (T) new B1();
    } else {
        return (T) new B2();
    }
}

void main() {
    AWrapper<B1> rightWrapper = new AWrapper<>(true, B1.class); //edited
    AWrapper<B1> wrongWrapper = new AWrapper<>(false, B1.class);  //edited, now this throws exception just like we hoped

    B1 rightObject = rightWrapper.getA();
    B1 wrongObject = wrongWrapper.getA(); //never gets here
}

В приведенном выше коде класс сохраняется, чтобы впоследствии можно было переназначить объект T, но если присвоение выполняется только один раз в конструкторе, тогда строку Class<? super T> tClass; можно удалитьполностью.При удалении этой строки очевидно все типы безопасности без снижения производительности (кроме фактического оператора if).

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