Примеры использования дженериков на Java - PullRequest
8 голосов
/ 03 апреля 2009

Может кто-нибудь указать мне на хорошие примеры использования дженериков в Java? Под этим я подразумеваю примеры написания общего класса?

Большинство объяснений гласят: «Вы можете определить общий класс подобным образом. Теперь посмотрите API Java Collections и забудьте обо всем этом - просто используйте его и будьте счастливы».

То, что я хочу, больше похоже на то, что «вы можете определить общий класс, подобный этому, теперь рассмотрим этот случай, где вы, возможно, захотите его, теперь давайте посмотрим, как мы его напишем».

Спасибо.

Ответы [ 8 ]

8 голосов
/ 03 апреля 2009

Обобщения в Java облегчают параметрический полиморфизм . С помощью параметров типа вы можете передавать аргументы типам. Так же, как метод, подобный String foo(String s), моделирует некоторое поведение, не только для конкретной строки, но и для любой строки s, так и тип, подобный List<T>, моделирует некоторое поведение, не только для определенного типа, но для любой тип . List<T> говорит, что для любого типа T, существует List из T s . Так что List на самом деле является конструктором типа . Он принимает тип в качестве аргумента и создает другой тип в качестве результата.

Вот пара примеров универсальных типов, которые я использую каждый день. Во-первых, очень полезный универсальный интерфейс:

public interface F<A, B> {
  public B f(A a);
}

Этот интерфейс говорит, что для любых двух типов, A и B, есть функция (называемая f), которая принимает A и возвращает B. Когда вы При реализации этого интерфейса A и B могут быть любых типов, которые вы хотите, при условии, что вы предоставляете функцию f, которая берет первое и возвращает второе. Вот пример реализации интерфейса:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

До появления дженериков полиморфизм достигался с помощью подкласса с использованием ключевого слова extends. С помощью дженериков мы можем фактически покончить с подклассами и использовать вместо этого параметрический полиморфизм. Например, рассмотрим параметризованный (универсальный) класс, используемый для вычисления хеш-кодов для любого типа. Вместо переопределения Object.hashCode () мы бы использовали общий класс, подобный этому:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

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

Обобщения Java не идеальны, хотя. Вы можете абстрагироваться от типов, но вы не можете абстрагироваться от конструкторов типов, например. То есть вы можете сказать «для любого типа T», но нельзя сказать «для любого типа T, который принимает параметр типа A».

Я написал статью об этих пределах обобщений Java, здесь.

Источник для множества полезных универсальных типов можно найти здесь. . Также проверьте исходный код стандартной библиотеки Java, если вы этого еще не сделали.

3 голосов
/ 03 апреля 2009

На ваш ответ частично дан ответ в FAQ по родовым Java * , который дает несколько хороших примеров того, когда вы хотите, чтобы ваш класс был универсальным. Также ознакомьтесь с разделом Разработка общих методов в этом FAQ.

2 голосов
/ 03 апреля 2009
public class Pair<A,B> {
    private A a;
    private B b;
    public Pair(A a, B b) { setFirst(a); setSecond(b); }    
    public A getFirst() { return a; }
    public B getSecond() { return b; }
    public void setFirst(A a) { this.a=a; } 
    public void setSecond(B b) { this.b=b; }
}
2 голосов
/ 03 апреля 2009

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

public class TrieMap<C extends Comparable<C>, K extends ComponentComparable<C>, V>
 extends AbstractMap<K,V> implements SortedMap<K,V> {
    ...
}

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

public abstract interface ComponentComparable<C extends Comparable<C>>
 extends Comparable<ComponentComparable<C>>, Iterable<C> {
    ...
}

Я также создал целый набор классов узлов, таких как

public interface Node<T extends Node<T>> { ... }
public interface TreeNode<T extends TreeNode<T>> extends Iterable<T>, Node<T> { ... }
public interface ListNode<T extends ListNode<T>> extends Node<T> { ... }
public interface BinaryTreeNode<T extends BinaryTreeNode<T>> extends Node<T> { ... }
public interface TernaryTreeNode<T extends TernaryTreeNode<T>>
 extends BinaryTreeNode<T> { ... }

, который может пригодиться для создания графиков, деревьев, связанных списков и т. Д. Вместе с классом Algorithms, который имеет все виды (универсальных!) Методов для работы на этих узлах.

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

public abstract interface Convertable<T> {
    public abstract T convert();
}

или один для парсеров строк:

public interface Parser<O> {
    public abstract O parse(String str);
}

или один для классов, которые являются их собственными парсерами:

public interface Parseable<P extends Parseable<P>> extends Parser<P> {}

, который я использовал при создании системы для разбора параметров командной строки:

public abstract class Option<T> { ... }
public class CLOption<P extends Parseable<P>> extends Option<P> { ... }
public class StringOption extends Option<String> { ... }

и т.д.. и т. д.

2 голосов
/ 03 апреля 2009

Я использовал обобщения для работы с некоторыми основными операциями CRUD в DAO. Большая часть кода была бы продублирована для различных доменных объектов, поэтому, насколько это возможно, я извлекся в общий абстрактный класс.

1 голос
/ 03 апреля 2009

Пример статической служебной функции с использованием обобщений. Он берет две карты, A -> B и B -> C, и строит из нее карту A -> C (хэш).

public static <A, B, C> Map<A, C> transitise(Map<A, B> aToB, Map<B, C> bToC) {
    HashMap<A, C> map = new HashMap<A, C>(aToB.size() * 2);
    for (Map.Entry<A, B> abEntry : aToB.entrySet()) {
        map.put(abEntry.getKey(), bToC.get(abEntry.getValue()));
    }
    return map;
}

Пример использования:

public static void main(String[] args) {
    HashMap<String, Integer> nameToNumber = new HashMap<String, Integer>();
    nameToNumber.put("Anna", 12345);
    nameToNumber.put("James", 444);
    HashMap<Integer, Point> numberToPosition = new HashMap<Integer, Point>();
    numberToPosition.put(12345, new Point(3, 3));
    numberToPosition.put(444, new Point(1, 55));
    for (Map.Entry<String, Point> nTP :
        transitise(nameToNumber, numberToPosition).entrySet())
    {
        System.out.println(nTP.getKey() + " -> " +
            nTP.getValue().x + "/" + nTP.getValue().y);
    }
}

Результат:

James -> 1/55
Anna -> 3/3

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

0 голосов
/ 03 апреля 2009

Я думаю, что хорошими примерами обобщений являются классы коллекций Java SE.
Вы получили хорошую документацию и много исходного кода для изучения.

0 голосов
/ 03 апреля 2009

Пара полезных упражнений:

  • написать собственную реализацию ArrayList
  • написать базовую реализацию Map (не нужно беспокоиться об эффективности, если вам нужно пройти через весь массив, чтобы найти что-то, сделайте это)
  • подкласс вашей карты вкл. для подсчета значений записи для ключа с помощью пользовательского Map.Entry, у которого есть метод, который возвращает количество записей

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

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