Избегать непроверенных приведений с дженериками с двойными расширяющимися классами?

У меня есть следующий код, который я только что рефакторинг на это сегодня, так как я понимаю, что <T extends Buffer> действительно означает, вот упрощенная версия:

public class Buffer {
    protected final int bufferType;
    protected final int bufferDataType;

    protected int bufferId;
    protected boolean created;

    public Buffer(final int bufferType, final int bufferDataType) {
        this.bufferType = bufferType;
        this.bufferDataType = bufferDataType;
    }

    public <T extends Buffer> T create() {
        assertNotCreated();
        bufferId = GL15.glGenBuffers();

        created = true;
        return (T)this;
    }

    public boolean hasBeenCreated() {
        return created;
    }

    private void assertNotCreated() {
        if (hasBeenCreated()) {
            throw new RuntimeException("Buffer has been created already.");
        }
    }
}

public class ArrayBuffer extends Buffer {
    public ArrayBuffer(final int bufferDataType) {
        super(GL15.GL_ARRAY_BUFFER, bufferDataType);
    }    
}

public class DynamicDrawArrayBuffer extends ArrayBuffer {
    public DynamicDrawArrayBuffer() {
        super(GL15.GL_DYNAMIC_DRAW);
    }
}

Предупреждение происходит в Buffer.create()безопасно ли подавить предупреждение? Есть ли способ сделать его более безопасным?

Другое требование заключается в том, что в вызывающий / использующий код этого API не следует добавлять беспорядок, конкретно это означает, что DynamicDrawArrayBuffer может не привязываться к дженерикам.

2 ответа

Решение

Это явно не типобезопасно. Ты можешь написать

ArrayBuffer a = new ArrayBuffer(0);
DynamicDrawArrayBuffer d = a.create();

и это вызовет ClassCastException, Тип возвращаемого значения create метод выведен из сайта вызова. И единственная информация, которая доступна на сайте вызова здесь, это то, что возвращаемое значение должно быть DynamicDrawArrayBuffer - Итак ArrayBuffer жестоко брошен, чтобы быть одним.

Я думаю, что вы могли бы легко использовать ковариацию здесь: create Метод всегда может вернуть тип класса. Тип возвращаемого значения create Затем метод определяется информацией о типе, имеющейся у вас при вызове метода:

public class BufferTest
{
    public static void main(String[] args)
    {
        ArrayBuffer a = new ArrayBuffer();
        ArrayBuffer aa = a.create(); // Yes
        // DynamicDrawArrayBuffer ad = a.create(); // No

        DynamicDrawArrayBuffer d = new DynamicDrawArrayBuffer();
        ArrayBuffer da = a.create(); // Yes
        DynamicDrawArrayBuffer dd = d.create(); // Yes

        Buffer b = a;
        Buffer bb = b.create(); // Yes
        //ArrayBuffer ba = b.create(); // No
    }
}

class Buffer 
{
    public Buffer create() 
    {
        // create etc...
        return this;
    }

}

class DynamicDrawArrayBuffer extends ArrayBuffer 
{
    @Override
    public DynamicDrawArrayBuffer create()
    {
        super.create();
        return this;
    }
}

class ArrayBuffer extends Buffer 
{
    @Override
    public ArrayBuffer create()
    {
        super.create();
        return this;
    }
}

Еще более упрощенная версия:

public class Buffer {
    public <T extends Buffer> T create() {
        return (T)this;
    }

    public static class FooBuffer extends Buffer {}
    public static class BarBuffer extends FooBuffer {}

    public static void main(String... args) {
        BarBuffer b = new FooBuffer().create();
    }
}

main метод компилируется без предупреждений, выполнение кода приводит к ClassCastException, Предупреждение в create указывает на реальное отсутствие типов безопасности в коде.

Другие вопросы по тегам