Что хорошего в дженериках, зачем их использовать? - PullRequest
79 голосов
/ 17 сентября 2008

Я подумала, что предложу этот софтбол тому, кто захочет выбить его из парка. Что такое дженерики, каковы преимущества дженериков, почему, где, как я должен их использовать? Пожалуйста, оставьте это довольно простым. Спасибо.

Ответы [ 28 ]

4 голосов
/ 05 июня 2009

Разве вы никогда не писали метод (или класс), в котором ключевая концепция метода / класса не была тесно связана с конкретным типом данных параметров / переменных экземпляра (например, связанный список, функции max / min) , бинарный поиск и т. д.).

Разве вы не хотели, чтобы вы могли повторно использовать алгоритм / код, не прибегая к повторному использованию cut-n-paste или ставя под угрозу строгую типизацию (например, я хочу List строк, а не List вещей, которые я надеюсь это струны!)?

Вот почему вы должны хотеть использовать дженерики (или что-то лучше).

3 голосов
/ 05 июня 2009

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

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

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

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

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

Это просто простой пример использования универсальных методов. Есть немало других полезных вещей, которые вы можете сделать с помощью универсальных методов. Самым крутым, на мой взгляд, является вывод типа с дженериками. Возьмите следующий пример (взято из Effective Java 2nd Edition Джоша Блоха):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Это не очень много, но оно сокращает некоторые помехи, когда универсальные типы длинные (или вложенные; т.е.

2 голосов
/ 05 июня 2009

Из документации Sun Java, в ответ на вопрос «почему я должен использовать дженерики?»:

"Generics предоставляет вам способ сообщать компилятору тип коллекции, чтобы его можно было проверить. Как только компилятор узнает тип элемента коллекции, компилятор может проверить, что вы использовали коллекцию последовательно и может вставлять правильные преобразования значений, извлекаемых из коллекции ... Код, использующий обобщенные значения, более понятен и безопасен ... компилятор может проверить во время компиляции, что ограничения типа не нарушаются во время выполнения [выделено мной]. Поскольку программа компилируется без предупреждений, мы можем с уверенностью заявить, что она не будет генерировать исключение ClassCastException во время выполнения. Чистый эффект от использования обобщений, особенно в больших программах, улучшает читаемость и надежность . [Выделение мое] "

2 голосов
/ 17 сентября 2008

Основное преимущество, как указывает Митчел, это строгая типизация без необходимости определения нескольких классов.

Таким образом, вы можете делать такие вещи, как:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

Без обобщений вам придется привести blah [0] к правильному типу, чтобы получить доступ к его функциям.

2 голосов
/ 17 сентября 2008

в любом случае jvm выполняет приведение ... он неявно создает код, который обрабатывает универсальный тип как "объект" и создает приведение к нужной реализации. Обобщения Java - просто синтаксический сахар.

2 голосов
/ 17 сентября 2008

Я знаю, что это вопрос C #, но дженерики используются и на других языках, и их использование / цели очень похожи.

В коллекциях Java используется обобщений , начиная с Java 1.5. Поэтому их лучше использовать, когда вы создаете свой собственный объект, похожий на коллекцию.

Примером, который я вижу почти везде, является класс Pair, который содержит два объекта, но должен обрабатывать эти объекты в общем виде.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

Всякий раз, когда вы используете этот класс Pair, вы можете указать, с какими объектами вы хотите, чтобы он имел дело, и любые проблемы приведения типов будут отображаться во время компиляции, а не во время выполнения.

Обобщения могут также иметь свои границы, определенные с ключевыми словами «super» и «extends». Например, если вы хотите иметь дело с универсальным типом, но хотите убедиться, что он расширяет класс с именем Foo (у которого есть метод setTitle):

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

Хотя это и не очень интересно само по себе, полезно знать, что всякий раз, когда вы имеете дело с FooManager, вы знаете, что он будет обрабатывать типы MyClass и что MyClass расширяет Foo.

1 голос
/ 17 сентября 2008

Несколько вещей для добавления / расширения (если говорить с точки зрения .NET):

Универсальные типы позволяют создавать ролевые классы и интерфейсы. Об этом уже говорилось в более простых терминах, но я обнаружил, что вы начинаете разрабатывать свой код с классами, которые реализуются независимо от типа, что приводит к многократно используемому коду.

Общие аргументы в отношении методов могут делать то же самое, но они также помогают применять принцип «Не спрашивай» к кастингу, т. Е. «Дай мне то, что я хочу, а если не можешь, скажи мне, почему» .

1 голос
/ 17 сентября 2008

Обобщения позволяют использовать строгую типизацию для объектов и структур данных, которые должны содержать любой объект. Это также устраняет утомительные и дорогие типы при извлечении объектов из общих структур (бокс / распаковка).

Один пример, который использует оба, является связанным списком. Что хорошего в классе связанного списка, если он может использовать только объект Foo? Чтобы реализовать связанный список, который может обрабатывать любой тип объекта, связанный список и узлы во внутреннем классе гипотетического узла должны быть универсальными, если вы хотите, чтобы список содержал только один тип объекта.

1 голос
/ 17 сентября 2008

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

1 голос
/ 05 июня 2009

Я использую их, например, в GenericDao, реализованном с помощью SpringORM и Hibernate, который выглядит следующим образом

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

При использовании обобщений мои реализации этого DAO заставляют разработчика передавать им только те сущности, для которых они предназначены, просто подклассифицируя GenericDao

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

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

Я, как и Стив с тобой, сказал в начале «Слишком грязно и сложно» но теперь я вижу его преимущества

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