【问题标题】:Java home grown generics examplesJava 自制泛型示例
【发布时间】:2009-04-02 21:00:11
【问题描述】:

谁能指出在 Java 中使用泛型的好例子?我的意思是自己编写泛型类的示例?

大多数解释是“你可以像这样定义一个泛型类。现在看看 Java 集合 API 并忘记所有这些 - 只需使用它并快乐。”

我想要的更像是“你可以像这样定义一个泛型类,现在考虑一下你可能需要它的这种情况,现在让我们看看我们将如何编写它。”

谢谢。

【问题讨论】:

    标签: java generics


    【解决方案1】:

    Java 中的泛型有助于parametric polymorphism。通过类型参数,您可以将参数传递给类型。就像String foo(String s) 这样的方法模拟一些行为,不仅针对特定字符串,而且针对任何字符串s,所以像List<T> 这样的类型模拟一些行为,不仅针对特定类型,而是为任何类型List<T> 表示对于任何类型的T,都存在ListTs。所以List 实际上是一个类型构造函数。它将一个类型作为参数并构造另一个类型作为结果。

    这里有几个我每天使用的泛型类型的例子。首先,一个非常有用的通用接口:

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

    这个接口表示对于任何两种类型,AB,都有一个函数(称为f)接受A 并返回B 当你实现这个接口,AB 可以是你想要的任何类型,只要你提供一个函数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”,但不能说“对于任何带有类型参数 A 的类型 T”。

    I wrote an article about these limits of Java generics, here.

    The source for a lot of useful generic types can be found here.。如果您还没有,请查看标准 Java 库的源代码。

    【讨论】:

      【解决方案2】:

      Java Generics FAQ 中部分回答了您的答案,它提供了一些很好的示例,说明您何时希望您的课程成为通用课程。另请查看该常见问题解答中的Designing Generic Methods 部分。

      【讨论】:

        【解决方案3】:

        我使用泛型来处理 DAO 中的一些基本 CRUD 操作。对于各种领域对象,大部分代码都会重复,所以我尽可能将其提取到一个通用抽象类中。

        【讨论】:

        【解决方案4】:

        不确定这是否正是您要寻找的,但就示例而言...您创建的任何类型的数据结构都可以(并且可能应该)以通用形式完成。我曾经这样开始过:

        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> { ... }
        

        等等。等等等等。

        【讨论】:

          【解决方案5】:
          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; }
          }
          

          【讨论】:

          • 我喜欢自己让它不可变。
          【解决方案6】:

          使用泛型的静态实用程序函数示例。它需要两个映射 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
          

          无论如何,我要说明的是,使用泛型,您可以制作一些非常有用的,呃,泛型实用函数。

          【讨论】:

            【解决方案7】:

            几个有用的练习:

            • 编写自己的 ArrayList 实现
            • 写一个基本的 Map 实现(不用担心效率问题,如果需要遍历整个数组来查找东西,就去做吧)
            • 子类化您的 Map impl。使用具有返回写入计数的方法的自定义 Map.Entry 计算键的值写入次数

            我不能马上给你代码示例,因为我正在处理它们,但也许你可以尝试自己做,如果你遇到困难,再回来提出更多问题。

            【讨论】:

            • 任何你真正想用数组实现的低级东西都可能是一个糟糕的例子。
            猜你喜欢
            • 2011-06-09
            • 1970-01-01
            • 2013-09-18
            • 1970-01-01
            • 2017-08-18
            • 2011-06-13
            • 2016-08-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多