2012-04-09 4 views
3

Если у меня есть метод, напримервозвращаемого типа универсальных методов

public INode getNode(final int offset); 

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

public <T extends INode> T getNode(final int offset); 

Или я что-то пропустил? Я думаю, что общие типы возвращаемых значений имеют значение только в том случае, если один из параметров имеет один и тот же тип (или супер/подтип)?

+0

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

+0

В чем вопрос? Что «не добавляет чего-то» означает? –

ответ

4
public <T extends INode> T getNode(final int offset); 

Это не только не дает никакой дополнительной информации к абоненту, но прямо опасно: Единственный способу реализации этого метод подписи является использование непроверенного броска, который не может быть типобезопасным , поскольку параметры типа метода задаются его вызывающим (явно или неявно через вывод типа), а параметры типа недоступны для реализации этого метода. Например, рассмотрим следующую программу:

class NodeCollection { 
    private INode[] nodes = new INode[42]; 

    public <T extends INode> T getNode(final int offset) { 
     return (T) nodes[offset]; 
    } 

    public <T extends INode> setNode(final int offset, T node) { 
     nodes[offset] = node; 
    } 
} 

class ANode implements INode {} 
class BNode implements INode { 
    void foo(); 
} 

public class Test { 
    public static void main(String[] args) { 
     NodeCollection nc = new NodeCollection(); 
     nc.setNode(0,new ANode()); 
     BNode b = nc.getNode(0); // throws ClassCastException (sic!) 
    } 
} 

Лучшая практика: Не использовать непроверенную бросок, если вы не действительно, что это будет тип правильно во время выполнения.

Я думаю, что общие типы возвращаемого значения имеют значение только в том случае, если один из параметров одного типа (или супер/подтип)?

Есть несколько случаев, например:

public <T> T getFavorite(Class<T> clazz) { 
    return clazz.cast(favorites.get(clazz)); 
} 

или

interface List<E> { 
    E get(int index); 
} 

или примеры в ответ Колина, где переменная типа только появляется в качестве параметра типа в обратном тип, который является приемлемым из-за стирания типа.

Edit:

Я думаю, что есть тип не сохранить путь, если один хочет, чтобы бросить к точному типу узла (вместо InstanceOf должен предшествовать ей)

Конечно, есть есть, это называется visitor pattern.

+0

Мы сохраняем узлы как INodes, то есть интерфейса INode, так как каждый узел должен его реализовать. Тогда у нас есть интерфейс IStructNode для «структурных» узлов. Курсор транзакций предоставляет вышеупомянутый метод, например moveTo (nodeID), тогда как он внутренне вызывает getNode (смещение). Другим методом является метод getNode(), который поставляет узлы как INodes, и getStructuralNode() как IStructNode или завернутый в «NullNode», который реализует шаблон нулевого объекта. Я думаю, что нет способа сохранения типа, если вы хотите передать точный тип узла (вместо instanceof должен предшествовать ему) – Johannes

+0

Было ли это предположено изменить ваш вопрос? Если это так, то, пожалуйста, используйте кнопку редактирования вопроса. Это также позволит вам структурировать и отформатировать поправку. – meriton

+0

Да, конечно, шаблон посетителя ;-) Я реализовал его примерно год назад, но просто не думал об этом ... ах. – Johannes

1

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

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


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

public static <T> Collection<T> generateCollection() { 
    return new ArrayList<T>(); 
} 

Это позволяет создать объект Collection от T без необходимости делать актеры.

Или, если вы хотите, чтобы разработчик делает реализацию, чтобы бросить его объект (в основном работает, когда указанный объект использует дженерики), пример из Collections:

public static final <T> Set<T> emptySet() { 
    return (Set<T>) EMPTY_SET; 
} 

Ресурсы:

Смежные вопросы