• Item27--优先考虑泛型方法


    类型推导:发生在以下三个地方。1.Java编译器根据泛型方法传入的参数,推导出具体的类型。2.Java编译器,根据泛型构造器传入的类型来推导出实际要构造的实例类型。3.Java编译器根据表达式的目标类型(TargetType)来确定出实际的类型参数。

    一、类型推导的例子:

    1.通过泛型方法的实际参数类型进行推导。
    比如:
           public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
                  Set<E> result = new HashSet<E>(s1);
                  result.addAll(s2);
                   return result;
           }
    在实际应用时,Java编译器根据传递的参数类型推导出实际的类型参数。
    Java编译器,根据传入的参数guys,stooges推导出类型参数E的实际类型是String。
    public class Union {
     
            // Generic method
            public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
                  Set<E> result = new HashSet<E>(s1);
                  result.addAll(s2);
                   return result;
           }
     
            // Simple program to exercise generic method
            public static void main(String[] args) {
                  Set<String> guys = new HashSet<String>(Arrays.asList( "Tom", "Dick" ,
                                "Harry"));
                  Set<String> stooges = new HashSet<String>(Arrays.asList( "Larry", "Moe",
                                "Curly"));
                  Set<String> aflCio = union(guys, stooges);
                  System. out.println(aflCio);
           }
    }
     
    2.通过泛型构造器的参数来推导实际类型
    比如
    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    .....
    }
    使用:
    通过<>中传递的参数,Java编译器会做类型推导,从而确定出创建的实例类型
     Map<String,List<String>> anagrams = new HashMap<String,List<String>>();
     
    3.在Java SE 8中提供的机制,通过一个表达式的Target Type(目标类型),Java编译器可以推导出实际的类型参数是什么
     
    比如
    public class GenericStaticFactory {
            // Generic static factory method
            public static <K, V> HashMap<K, V> newHashMap() {
                   return new HashMap<K, V>();
           }
     
            public static void main(String[] args) {
                   // Parameterized type instance creation with static factory
                  Map<String, List<String>> anagrams = newHashMap ();
                
           }
    }
    目标类型是<String,List<String>,Java编译器根据这个,就可以推导出,对应的<K,V>分别是String,List<String>。
    在使用newHashMap时,没有外部的类型参数输入,不像泛型构造器(通过构造器旁边的<>提供类型参数),也不像有参数的泛型方法(通过输入的参数类型,Java编译器可以推导出实际的类型参数),Java编译器是根据表达式的目标类型(Target Type)来确定的。
     
    又比如
    public class Collections {
     
      public static final List EMPTY_LIST = new EmptyList<Object>();
      
      @SuppressWarnings("unchecked")
          public static final <T> List<T>  emptyList() {
              return (List<T>) EMPTY_LIST;
         }
      
      /**
       * @serial include
       */
      private static class EmptyList<E>
          extends AbstractList<E>
          implements RandomAccess, Serializable {
         ....
      }
    }
    使用:
    public class TargetType {
     
     
     
      public void processStringList(List<String> stringList) {
        // process stringList
      }
     
      public static void main(String[] args) {
     
        TargetType one = new TargetType();
        one.processStringList(Collections.emptyList());
     
      }
     
     
    }
     
    二、泛型单例工厂---类型推导的重要应用
     
    public class GenericSingletonFactory {
            // Generic singleton factory pattern
            private static UnaryFunction<Object> IDENTITY_FUNCTION = new UnaryFunction<Object>() {
                   public Object apply(Object arg) {
                          return arg;
                  }
           };
     
            // IDENTITY_FUNCTION is stateless and its type parameter is
            // unbounded so it's safe to share one instance across all types.
            @SuppressWarnings("unchecked" )
            public static <T> UnaryFunction<T> identityFunction () {
                   return (UnaryFunction<T>) IDENTITY_FUNCTION;
           }
     
            // Sample program to exercise generic singleton
            public static void main(String[] args) {
                  String[] strings = { "jute", "hemp" , "nylon" };
                  UnaryFunction<String> sameString = identityFunction();
                   for (String s : strings)
                         System. out.println(sameString.apply(s));
     
                  Number[] numbers = { 1, 2.0, 3L };
                  UnaryFunction<Number> sameNumber = identityFunction();
                   for (Number n : numbers)
                         System. out.println(sameNumber.apply(n));
           }
    }
     
    1.什么时候使用泛型单例工厂?
    创建不可变,但又适合于不同类型的对象,这是使用场景。
    不同的类型通过类型参数来区别,那么,就只需要创建一个固定的对象,但是该对象的类型由类型参数来指定。
    2.为什么可以使用泛型实现?
    泛型在编译的时候,会把一个具体的类型擦除为Object。这也意味着,当你使用泛型的时候,即使你使用的类型参数都是不一样的,但是,最终在编译的时候都会被擦除为Object,所以,可以用一个Object来表示所有的类型。
    3.警告是什么意思?
    因为,对于UnaryFunction<Object> 对于每个T来说,并非都是UnaryFunction<T>。也就是说,我们并没有确保类型是安全的,我们可能传入的是String,但类型转换的时候是Number。不过,在这里,我们并没有修改返回的UnaryFunction<T>,我们只是传递一个类型参数。
     
    又一个例子:
    假设有一个如下的接口: 
     
    public interface NumberWrapper<T extends Number> {
         public double square(T num);
    }
     
    这个接口可以对Number的子类进行封装,它提供了一个方法可以输出值的平方。对于这样的Wrapper,我并不想对于每一个T都实例化一个相应的对象,那样有一些浪费,这时,就可以使用这个泛型单例工厂来生成支持不同T的单例。另外,因为NumberWrapper的泛型信息在运行时是会被擦除的,所以也没有必要对每一个T生成一个实例。泛型单例工厂的代码如下: 
     
    public class GenericFactory {

         private static NumberWrapper<Number> numWrapper = new NumberWrapper<Number>() {
              @Override
              public double square(Number num) {
                   return num.doubleValue() * num.doubleValue();
              }
         };

         @SuppressWarnings("unchecked")
         public static <T extends Number> NumberWrapper<T> getWrapperInstance() {
              return (NumberWrapper<T>) numWrapper;
         }

         public static void main(String[] args) {
              NumberWrapper<Integer> integerWrapper = GenericFactory.getWrapperInstance();
              System.out.println(integerWrapper.square(2));
             
              NumberWrapper<Double> doubleWrapper = GenericFactory.getWrapperInstance();
              System.out.println(doubleWrapper.square(0.05));
         }

    }
    这里,有一个点要注意下,就是getWrapperInstance()方法的的类型转换,这里是一个NumberWrapper<Number>向NumberWrapper<T>的转换,这里,由于square()方法返回的仅仅是平方,并没有对numWrapper的状态进行修改,这里是不会导致类型错误的,所以可以放心的禁止这条警告了。 
     
     
     
    三、递归类型限制(recursive type bound)
     
    递归类型限制:通过某个包含该类型参数本身的表达式来限制类型参数是允许的。这就是递归类型限制。
     
     
    public interface Comparable<T> {
     
      int compareTo(T o);
    }
    这个的含义:某个类型实现了Comparable接口。
    某个类型只能与它自身的类型的元素进行比较,该类型要实现Comparable接口。String只能与String类型的元素比较。
     
    public static <T extends Comparable<T> > T max(List<T>  list)
    { ...}
    <T extends Comparable<T>>的含义是:“针对可以与自身进行比较的每个类型T”
     
    这样,就可以实现一个泛型方法,该泛型方法只关注操作的步骤,不关注具体的类型,把操作和具体类型分离开来:
    public static <T extends Comparable<T>> T max(List<T>  list){
     
       Iterator<T> i = list.iterator():
       T result = i.next();
       while( i.hasNext()){
     
           T t = i.next();
           if( t.compareTo(result) > 0)
                result = t;
     
        }
     
      return result;
    }
     
    参考资料:
    https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
    http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Collections.java#Collections.0EMPTY_LIST
    http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java
    http://zddava.iteye.com/blog/375950
  • 相关阅读:
    Asp.net MVC 中Ajax的使用
    MVC Controller return 格式分类及用法
    【金楽】老博客地址
    C语言博客作业--结构体
    结构体、共用体、枚举博客转载
    C博客作业--指针
    C语言博客作业--字符数组
    C语言博客作业--一二维数组
    C语言博客作业--数据类型
    C语言博客作业--函数
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4420394.html
Copyright © 2020-2023  润新知