Generics


  • The term "generic" means "pertaining or appropriate to large groups of classes."
  • While using someone else’s generic type is fairly easy, when creating your own you will encounter a number of surprises.

Comparison with C++

  • Understanding certain aspects of C++ templates will help you understand the foundations of the concept, as well as the limitations of what you can do with Java generics and why.
  • The ultimate goal is to give you a clear understanding of where the boundaries lie.

Simple generics

  • There are some cases where you want a container to hold multiple types of objects, but typically you only put one type of object into a container.
  • One of the primary motivations for generics is to specify what type of object a container holds, and to have that specification backed up by the compiler.
  • That’s the core idea of Java generics: You tell it what type you want to use, and it takes care of the details.

A tuple library

  • One of the things you often want to do is return multiple objects from a method call.
  • A tuple is simply a group of objects wrapped together into a single object.
  • Each object in the tuple can be of a different type.
  • The longer-length tuples can be created with inheritance.
  • Because of generics, you can easily create any tuple to return any group of types, just by writing the expression.

Generic interfaces

  • The generator knows how to create new objects without any extra information.
  • Typically, a generator just defines one method, the method that produces new objects.
  • Using generics with interfaces is no different than using generics with classes.
  • One of the limitations of Java generics is that you cannot use primitives as type parameters.

Generic methods

  • A generic method allows the method to vary independently of the class.
  • If it’s possible to make a method generic rather than the entire class, it’s probably going to be clearer to do so.
  • With a generic method, you don’t usually have to specify the parameter types, because the compiler can figure that out for you.

Leveraging type argument inference

  • Type argument inference in a generic method can produce some simplification.
  • Type argument inference eliminates the need to repeat the generic parameter list.

Explicit type specification

  • It is possible to explicitly specify the type in a generic method, although the syntax is rarely needed.

Anonymous inner classes

  • Generics can also be used with inner classes and anonymous inner classes.

The mystery of erasure

  • All you find out is the identifiers that are used as the parameter placeholders.
  • There’s no information about generic parameter types available inside generic code.
  • You just can’t know the actual type parameter(s) used to create a particular instance.
  • Any specific type information is erased when you use a generic.
  • Understanding erasure and how you must deal with it will be one of the biggest hurdles you will face when learning Java generics

The C++ approach

  • Writing this kind of code in C++ is straightforward because when a template is instantiated, the template code knows the type of its template parameters.
  • We must assist the generic class by giving it a bound that tells the compiler to only accept types that conform to that bound.
  • A generic type parameter erases to its first bound.
  • The compiler actually replaces the type parameter with its erasure.
  • Generics are only useful when you want to use type parameters that are more "generic" than a specific type.
  • The type parameters and their application in useful generic code will usually be more complex than simple class replacement.

Migration compatibility

  • It is a compromise in the implementation of Java generics, necessary because generics were not made part of the language from the beginning.
  • Erasure reduces the "genericity" of generics.
  • The generic types are present only during static type checking, after which every generic type in the program is erased by replacing it with a non-generic upper bound.
  • The core motivation for erasure is that it allows generified clients to be used with non-generified libraries, and vice versa.
  • Erasure enables this migration towards generics by allowing non-generic code to coexist with generic code.
  • To achieve migration compatibility, each library and application must be independent of all the others regarding whether generics are used.

The problem with erasure

  • Erasure allows existing nongeneric client code to continue to be used without change, until clients are ready to rewrite code for generics.
  • You must constantly be reminding yourself that it only appears that you have type information about a parameter.

The action at the boundaries

  • Using Array.newInstance( ) is the recommended approach for creating arrays in generics.
  • Even though erasure removes the information about the actual type inside a method or class, the compiler can still ensure internal consistency in the way that the type is used within the method or class.
  • What matters at run time is the boundaries: the points where objects enter and leave a method.

Compensating for erasure

  • Sometimes you must compensate for erasure by introducing a type tag. This means you explicitly pass in the Class object for your type so that you can use it in type expressions.
  • The compiler ensures that the type tag matches the generic argument.

Creating instances of types

  • If you use a type tag, you can use newlnstance( ) to create a new object of that type.

Arrays of generics

  • You can’t create arrays of generics. The general solution is to use an ArrayList everywhere that you are tempted to create an array of generics.
  • You can define a reference in a way that makes the compiler happy. This does in fact compile, but it won’t run.
  • The only way to successfully create an array of a generic type is to create a new array of the erased type, and cast that.
  • There’s no way to subvert the type of the underlying array, which can only be Object[].
  • It’s best to not issue any kind of message from the compiler unless the programmer must do something about it.
  • If certain idioms appear in the Java library sources, that’s not necessarily the right way to do it.

Bounds

  • Bounds allow you to place constraints on the parameter types that can be used with generics.
  • This allows you to enforce rules about the types that your generics can be applied to.
  • A potentially more important effect is that you can call methods that are in your bound types.
  • If you are able to constrain that parameter to be a subset of types, then you can call the methods in that subset.
  • It’s important for you to understand that extends has a significantly different meaning in the context of generic bounds than it does ordinarily.

Wildcards

  • It’s clear that the array objects can preserve the rules about the type of objects they contain.
  • Because arrays are completely defined in the language and can thus have both compile-time and runtime checks built in.
  • But with generics, the compiler and runtime system cannot know what you want to do with your types and what the rules should be.

How smart is the compiler?

  • It’s up to the generic class designer to decide which calls are "safe," and to use Object types for their arguments.
  • To disallow a call when the type is used with wildcards, use the type parameter in the argument list.

Contravariance

  • You say that the wildcard is bounded by any base class of a particular class, by specifying <? super MyClass> or even using a type parameter: <? super T>
  • You can thus begin to think of subtype and supertype bounds in terms of how you can "write" (pass into a method) to a generic type, and "read" (return from a method) from a generic type.
  • If you can get away with a static generic method, you don’t necessarily need covariance if you’re just reading.

Unbounded wildcards

  • The unbounded wildcard <?> appears to mean "anything," and so using an unbounded wildcard seems equivalent to using a raw type.
  • <?> can be thought of as a decoration.
  • When you are dealing with multiple generic parameters, it’s sometimes important to allow one parameter to be any type while establishing a particular type for the other parameter.
  • List actually means "a raw List that holds any Object type," whereas List<?> means "a non-raw List of some specific type, but we just don’t know what that type is."
  • So anytime you have a raw type, you give up compile-time checking.
  • The benefit of using exact types instead of wildcard types is that you can do more with the generic parameters.
  • Using wildcards allows you to accept a broader range of parameterized types as arguments.

Capture conversion

  • The unspecified wildcard type is captured and converted to an exact type.
  • Capture conversion only works in situations where, within the method, you need to work with the exact type.

Issues

No primitives as type parameters

  • Autoboxing doesn’t apply to arrays.

Implementing parameterized interfaces

  • A class cannot implement two variants of the same generic interface.

Casting and warnings

  • Using a cast or instanceof with a generic type parameter doesn’t have any effect.

Overloading

  • Overloading the method produces the identical type signature because of erasure.

Self-bounded types

Curiously recurring generics

  • You can’t inherit directly from a generic parameter. However, you can inherit from a class that uses that generic parameter in its own definition.
  • Your class appears, rather curiously, in its own base class.
  • "I’m creating a new class that inherits from a generic type that takes my class name as its parameter."
  • Generics in Java are about arguments and return types, so it can produce a base class that uses the derived type for its arguments and return types.
  • This is the essence of CRG: The base class substitutes the derived class for its parameters.

Self-bounding

  • Self-bounding takes the extra step of forcing the generic to be used as its own bound argument.
  • The type parameter must be the same as the class being defined.
  • The self-bounding idiom is not enforceable.
  • The self-bounding constraint serves only to force the inheritance relationship.
  • If you use self-bounding, you know that the type parameter used by the class will be the same basic type as the class that’s using that parameter.

Argument covariance

  • A derived type method should be able to return a more specific type than the base type method that it’s overriding.
  • Without self-bounding, you overload on argument types. If you use self-bounding, you only end up with one version of a method, which takes the exact argument type.

Dynamic type safety

  • Because you can pass generic containers to pre-Java SE5 code, there’s still the possibility that old-style code can corrupt your containers.
  • A checked container will throw a ClassCastException at the point you try to insert an improper object, as opposed to a pre-generic (raw) container which would inform you that there was a problem when you pulled the object out.
  • It’s fine to put derived-type objects into a checked container that is checking for the base type.

Exceptions

  • A catch clause cannot catch an exception of a generic type, because the exact type of the exception must be known at both compile time and run time.
  • A generic class can’t directly or indirectly inherit from Throwable.
  • Type parameters may be used in the throws clause of a method declaration.

Mixins

  • The fundamental concept is that of mixing in capabilities from multiple classes in order to produce a resulting class that represents all the types of the mixins.
  • One value of mixins is that they consistently apply characteristics and behaviors across multiple classes.
  • Mixins have part of the flavor of aspect-oriented programming (AOP), and aspects are often suggested to solve the mixin problem.

Mixins in C++

  • A more interesting and elegant approach to mixins is using parameterized types, whereby a mixin is a class that inherits from its type parameter.
  • You can think of a mixin as a function that maps existing classes to new subclasses.
  • Java generics don’t permit this. Erasure forgets the base-class type, so a generic class cannot inherit directly from a generic parameter.

Mixing with interfaces

  • A commonly suggested solution is to use interfaces to produce the effect of mixins.

Using the Decorator pattern

  • Decorators are often used when, in order to satisfy every possible combination, simple subclassing produces so many classes that it becomes impractical.
  • Decorator specifies that all objects that wrap around your initial object have the same basic interface.
  • Decorators are implemented using composition and formal structures, whereas mixins are inheritance-based.
  • You could think of parameterized-type-based mixins as a generic decorator mechanism that does not require the inheritance structure of the Decorator design pattern.
  • A significant drawback to Decorator is that it only effectively works with one layer of decoration (the final one), and the mixin approach is arguably more natural.

Mixins with dynamic proxies

  • With a dynamic proxy, the dynamic type of the resulting class is the combined types that have been mixed in.

Latent typing

  • Code that doesn’t care what type it works with can indeed be applied everywhere, and is thus quite "generic."
  • A language with latent typing loosens the constraint (and produces more generic code) by only requiring that a subset of methods be implemented, not a particular class or interface.
  • By not requiring a specific type, your code can be more generic.
  • Latent typing is a code organization and reuse mechanism. With it you can write a piece of code that can be reused more easily than without it.
  • Latent typing does not require either static or dynamic type checking.
  • It initially seems that Java’s generic mechanism is "less generic" than a language that supports latent typing.

Using function objects as strategies

  • Strategy design pattern, which produces more elegant code because it completely isolates "the thing that changes" inside of a function object.
  • A function object is an object that in some way behaves like a function.
  • They can be passed around, and they can also have state that persists across calls.
  • We are creating function objects which perform adaptation, and they are being passed into methods to be used as strategies.

Summary: Is casting really so bad?

  • Without the Java SE5 generic version of the container, you put Objects in and you get Objects out.
  • Type-safe containers come as a side effect of the ability to create more general purpose code.
  • It is fairly easy to write truly generic "holder" classes (which the Java containers are), but to write generic code that manipulates its generic types requires extra effort, on the part of both the class creator and the class consumer, who must understand the concept and implementation of the Adapter design pattern.
  • Introducing any kind of generic mechanism in a later version of a language, after that language has come into general use, is a very, very messy proposition, and one that cannot be accomplished without pain.

Thinking in Java——笔记(15)的更多相关文章

  1. Java笔记15:多线程

    Java实现多线程有两种方式:一是继承Thread类:二是实现Runable接口. 一.Thread实现 publicclass ThreadDemo2 { publicstaticvoid main ...

  2. java笔记15之this

    this:是当前类的对象引用,记为该类的一个对象 注意:谁调用这个方法,在这个方法内部的this就是代表谁 解决场景: 解决局部变量隐藏成员变量 class Student { private Str ...

  3. JAVA自学笔记15

    JAVA自学笔记15 @例题1:共有5个学生,请把五个学生的信息存储到数组中,并遍历数组,并获取每个学生的信息 Students[] students=new Student[5]; Student ...

  4. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

  5. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  6. java笔记00-目录

    --2013年7月26日17:49:59 学习java已久,趁最近有空,写一个总结: java笔记01-反射:

  7. Ext.Net学习笔记15:Ext.Net GridPanel 汇总(Summary)用法

    Ext.Net学习笔记15:Ext.Net GridPanel 汇总(Summary)用法 Summary的用法和Group一样简单,分为两步: 启用Summary功能 在Feature标签内,添加如 ...

  8. 斯坦福ML公开课笔记15—隐含语义索引、神秘值分解、独立成分分析

    斯坦福ML公开课笔记15 我们在上一篇笔记中讲到了PCA(主成分分析). PCA是一种直接的降维方法.通过求解特征值与特征向量,并选取特征值较大的一些特征向量来达到降维的效果. 本文继续PCA的话题, ...

  9. [Aaronyang] 写给自己的WPF4.5 笔记15[AyArc诞生-WPF版本绚丽的环状图,Ay制作,AyWindow强势预览]

    原文:[Aaronyang] 写给自己的WPF4.5 笔记15[AyArc诞生-WPF版本绚丽的环状图,Ay制作,AyWindow强势预览]  我的文章一定要做到对读者负责,否则就是失败的文章  -- ...

随机推荐

  1. 可以正确显示表格线的Grid item view

    Android上要显示一个表格,没有Swing那么专门的JTable可用. 搜了下,一般用GridView,有诸多不便和需要自己实现的地方: 跟ListView一样的Adapter,getView的时 ...

  2. 最简单的js确认框!

    随便举个栗子~ function bremove() { if (ids == "") {//触发函数,如果值是空弹框 alert("您还没有选择任何数据.") ...

  3. java27

    1:反射(理解)    (1)类的加载及类加载器    (2)反射:        通过字节码文件对象,去使用成员变量,构造方法,成员方法    (3)反射的使用        A:通过反射获取构造方 ...

  4. MongoDB索引创建(5)

    索引创建 1:索引提高查询速度,降低写入速度,权衡常用的查询字段,不必在太多列上建索引 2. 在mongodb中,索引可以按字段升序/降序来创建,便于排序 3. 默认是用btree来组织索引文件,2. ...

  5. 解决Delphi图形化界面的TEdit、TLable等组件手动拖拽固定大小,但是编译之后显示有差别的情况

    经常遇到这样的情况,在我们使用Delphi的可视化工具进行UI设计的时候,我们拖拽TEdit或者Label组件,并且在可视化界面上设置它们的长.宽 但是当我们编译和运行程序的时候,却发现真正显示出来的 ...

  6. oracle存储过程截取字符串

    declare CURSOR l_c IS select classid from cjt_class; Begin FOR i IN l_c LOOP update cjt_class t set ...

  7. 分布式缓存技术redis学习系列(三)——redis高级应用(主从、事务与锁、持久化)

    上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...

  8. inner join on 和 where = 的区别!

    请看下面两条语句:select * from table1 inner join table2 on table1.id = table2.idselect * from table1,table2 ...

  9. pandas 学习(1): pandas 数据结构之Series

    1. Series Series 是一个类数组的数据结构,同时带有标签(lable)或者说索引(index). 1.1 下边生成一个最简单的Series对象,因为没有给Series指定索引,所以此时会 ...

  10. android解析json

    android2.3提供的json解析类 android的json解析部分都在包org.json下,主要有以下几个类: JSONObject:可以看作是一个json对象 JSONStringer:js ...