В чем разница между этими двумя объявлениями переменных Java? - PullRequest
2 голосов
/ 14 октября 2010
public class SomeClass {
    private HashSet<SomeObject> contents = new HashSet<SomeObject>();
    private Set<SomeObject> contents2 = new HashSet<SomeObject>();
}

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

Ответы [ 5 ]

22 голосов
/ 14 октября 2010

Set - это интерфейс, а HashSet - это класс, который реализует интерфейс Set.

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

Если вам не нужны какие-либо особые функции из HashSet, лучше объявить переменную типа Set.Это оставляет точную реализацию открытой для изменения позже.Вы можете обнаружить, что для данных, которые вы используете, лучше работает другая реализация.Используя интерфейс, вы можете внести это изменение позже, если это необходимо.

Более подробную информацию вы можете увидеть здесь: Когда мне следует использовать интерфейс в Java?

3 голосов
/ 14 октября 2010

Set - это интерфейс, который реализует HashSet, поэтому, если вы сделаете это:

Set<E> mySet = new HashSet<E>();

Вы по-прежнему будете иметь доступ к функциональности HashSet, но у вас также есть возможность заменить в будущем конкретный экземпляр на другой класс Set, такой как LinkedHashSet или TreeSet, или другая реализация.

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

Это пункт 52 из книги Джошуа Блоха Effective Java, 2nd Edition .

Обращайтесь к объектам по их интерфейсам

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

// Обычно хорошо - использует интерфейс как тип

List<T> tlist = new Vector<T>();

// Типично Плохо - в качестве типа используется конкретный класс!

Vector<T> vec = new Vector<T>();

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

Например, Vector<T> - это synchronized, тогда как ArrayList<T> (также реализатор List<T>) - нет, поэтому, если вам нужны синхронизированные контейнеры в вашем проекте (или нет), вам необходимо документировать это .

3 голосов
/ 14 октября 2010

Поскольку класс HashSet реализует интерфейс Set, его можно присвоить переменной Set.Однако вы не можете пойти другим путем (назначьте Set более определенной переменной HashSet).

3 голосов
/ 14 октября 2010

Set - это интерфейс коллекции, который реализует HashSet.

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

2 голосов
/ 14 октября 2010

Стоит отметить, что интерфейс с конкретным правилом класса наиболее важен для типов, представленных в API, например. параметр метода или тип возвращаемого значения. Для приватных полей и переменных это только гарантирует, что вы не используете какие-либо методы из конкретной реализации (например, HashSet), но тогда это приватно, так что на самом деле не имеет значения.

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

...