From: acolyer Date: Wed, 18 May 2005 06:38:16 +0000 (+0000) Subject: updates for generics (still a work in progress) X-Git-Tag: PRE_ANDY~309 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0ee39450faa1183fc0bfdaaa683e8ca2afea468d;p=aspectj.git updates for generics (still a work in progress) --- diff --git a/docs/adk15ProgGuideDB/annotations.xml b/docs/adk15ProgGuideDB/annotations.xml index 2cde28bef..8d41e9a0e 100644 --- a/docs/adk15ProgGuideDB/annotations.xml +++ b/docs/adk15ProgGuideDB/annotations.xml @@ -413,7 +413,7 @@ '!' TypePattern | '(' AnnotationPattern? TypePattern ')' TypePattern '&&' TypePattern | - TypePattern '||' TypePattern | + TypePattern '||' TypePattern SimpleTypePattern := DottedNamePattern '+'? '[]'* diff --git a/docs/adk15ProgGuideDB/generics.xml b/docs/adk15ProgGuideDB/generics.xml index 57ea7ae2d..5e0f5b51d 100644 --- a/docs/adk15ProgGuideDB/generics.xml +++ b/docs/adk15ProgGuideDB/generics.xml @@ -1,3 +1,4 @@ + Generics @@ -13,39 +14,921 @@ - Declaring Parameterized Types + Declaring Generic Types + + A generic type is declared with one or more type parameters following the type name. + By convention formal type parameters are named using a single letter, though this is not required. + A simple generic list type + (that can contain elements of any type E) could be declared: + + + { + Iterator iterator(); + void add(E anItem); + E remove(E anItem); + } + ]]> + + + + It is important to understand that unlike template mechanisms there will only be one type, and one class file, corresponding to + the List interface, regardless of how many different instantiations of the List interface a program + has (each potentially providing a different value for the type parameter E). A consequence of this + is that you cannot refer to the type parameters of a type declaration in a static method or initializer, or in the declaration or + initializer of a static variable. + + + A parameterized type + is an invocation of a generic type with concrete values supplied for + all of its type parameters (for example, List<String> or List<Food>). + + + A generic type may be declared with multiple type parameters. In addition to simple type parameter names, type + parameter declarations can also constrain the set of types allowed by using the extends + keyword. Some examples follow: + + + + + class Foo<T> {...} + + A class Foo with one type parameter, T. + + + + + + class Foo<T,S> {...} + + A class Foo with two type parameters, T and S. + + + + + + class Foo<T extends Number> {...} + + A class Foo with one type parameter T, where T must be + instantiated as the type Number or a subtype of Number. + + + + + + class Foo<T, S extends T> {...} + + A class Foo with two type parameters, T and S. Foo + must be instantiated with a type S that is a subtype of the type specified for parameter T. + + + + + + class Foo<T extends Number & Comparable> {...} + + A class Foo with one type parameter, T. Foo + must be instantiated with a type that is a subtype of Number and that implements Comparable. + + + + + + + + + + Using Generic and Parameterized Types + + You declare a variable (or a method/constructor argument) of a parameterized type by specifying a concrete type specfication for each type parameter in + the generic type. The following example declares a list of strings and a list of numbers: + + strings; + List numbers; + ]]> + + It is also possible to declare a variable of a generic type without specifying any values for the type + parameters (a raw type). For example, List strings. + In this case, unchecked warnings may be issued by the compiler + when the referenced object is passed as a parameter to a method expecting a parameterized type such as a + List<String>. New code written in the Java 5 language would not be expected to use + raw types. + + Parameterized types are instantiated by specifying type parameter values in the constructor call expression as in + the following examples: + + strings = new MyListImpl(); + List numbers = new MyListImpl(); + ]]> + + + When declaring parameterized types, the ? wildcard may be used, which stands for "some type". + The extends and super keywords may be used in conjunction with the wildcard + to provide upper and lower bounds on the types that may satisfy the type constraints. For example: + + + + + + List<?> + + A list containing elements of some type, the type of the elements in the list is unknown. + + + + + + List<? extends Number> + + A list containing elements of some type that extends Number, the exact type of the elements in the list is unknown. + + + + + + List<? super Double> + + A list containing elements of some type that is a super-type of Double, the exact type of the elements in the list is unknown. + + + + + + + + A generic type may be extended as any other type. Given a generic type Foo<T> then + a subtype Goo may be declared in one of the following ways: + + + + + + class Goo extends Foo + + Here Foo is used as a raw type, and the appropriate warning messages will be + issued by the compiler on attempting to invoke methods in Foo. + + + + + + class Goo<E> extends Foo + + Goo is a generic type, but the super-type Foo is used as a raw + type and the appropriate warning messages will be + issued by the compiler on attempting to invoke methods defined by Foo. + + + + + + class Goo<E> extends Foo<E> + + This is the most usual form. Goo is a generic type with one parameter that extends + the generic type Foo with that same parameter. So Goo<String< is + a subclass of Foo<String>. + + + + + + class Goo<E,F> extends Foo<E> + + Goo is a generic type with two parameters that extends + the generic type Foo with the first type parameter of Goo being used + to parameterize Foo. So Goo<String,Integer< is + a subclass of Foo<String>. + + + + + + class Goo extends Foo<String> + + Goo is a type that extends + the parameterized type Foo<String>. + + + + + + + A generic type may implement one or more generic interfaces, following the type binding + rules given above. A type may also implement one or more parameterized interfaces (for example, + class X implements List<String>, however a type may not at the same time + be a subtype of two interface types which are different parameterizations of the same interface. - Using Parameterized Types + Subtypes, Supertypes, and Assignability + + + The supertype of a generic type C is the type given in the extends clause of + C, or Object if no extends clause is present. Given the type declaration + + + extends Collection {... } + ]]> + + + then the supertype of List<E> is Collection<E>. + + + + The supertype of a parameterized type P is the type given in the extends clause of + P, or Object if no extends clause is present. Any type parameters in + the supertype are substituted in accordance with the parameterization of P. An example + will make this much clearer: Given the type List<Double> and the definition of + the List given above, the direct supertype is + Collection<Double>. List<Double> is not + considered to be a subtype of List<Number>. + + + + An instance of a parameterized type P<T1,T2,...Tn>may be assigned to a variable of + the same type or a supertype + without casting. In addition it may be assigned to a variable R<S1,S2,...Sm> where + R is a supertype of P (the supertype relationship is reflexive), + m <= n, and for all type parameters S1..m, Tm equals + Sm or Sm is a wildcard type specification and + Tm falls within the bounds of the wildcard. For example, List<String> + can be assigned to a variable of type Collection<?>, and List<Double> + can be assigned to a variable of type List<? extends Number>. + + - Assignments and Wildcards + Generic Methods and Constructors + + A static method may be declared with one or more type parameters as in the following declaration: + + + T first(List ts) { ... } + ]]> + + + Such a definition can appear in any type, the type parameter T does not need to + be declared as a type parameter of the enclosing type. + + + + Non-static methods may also be declared with one or more type parameters in a similar fashion: + + + T max(T t1, T t2) { ... } + ]]> + + The same technique can be used to declare a generic constructor. + - Generic Methods + Erasure + Generics in Java are implemented using a technique called erasure. All + type parameter information is erased from the run-time type system. Asking an object of a parameterized + type for its class will return the class object for the raw type (eg. List for an object + declared to be of type List<String>. A consequence of this is that you cannot at + runtime ask if an object is an instanceof a parameterized type. + Generics in AspectJ 5 - Parameterized Aspect Types + Matching generic and parameterized types in type patterns + + + The type pattern "Foo" matches all types named Foo, whether they + be simple types, generic types, or parameterized types. So for example, Foo, + Foo<T>, and Foo<String>will all be matched. + + + + AspectJ 5 also extends the specification of type patterns to allow explicit matching of generic and parameterized + types. + + + ' + + TypeParameterPatternList ::= TypeParameterPattern (',' TypeParameterPattern)* + + TypeParameterPattern ::= TypePattern | + '?' TypeBoundPattern? + + TypeBoundPattern ::= 'extends' TypePattern AdditionalBoundPatternList? | + 'super' TypePattern AdditionalBoundPatternList? + + AdditionalBoundPatternList ::= AdditionalBoundPattern AdditionalBoundPatternList | + AdditionalBoundPattern + + AdditionalBoundPattern ::= '&' TypePattern + + TypeParameterList ::= '<' TypeParameter (',' TypeParameter)* '>' + + TypeParameter ::= Identifier TypeBound? + + TypeBound ::= 'extends' ReferenceType AdditionBoundList? | + 'super' ReferenceType AdditionalBoundList? + + AdditionalBoundList ::= AdditionalBound AdditionalBoundList | + AdditionalBound + + AdditionalBound ::= '&' ReferenceType + + ]]> + + + A simple identifier occuring in a type parameter list will be treated as a type name unless it has previously + been declared as a type variable in a TypeParameterList. Some simple examples of + type patterns follow: + + + + + + List<String> + + Matches the parameterized type List<String> + + + + + + List<? extends Number> + + Matches the parameterized type List<? extends Number> + + + + + + List<E> + + Matches the parameterized type List<E>. If E is not + a type then an invalidAbsoluteTypeName xlint warning will be issued. + + + + + + <E> List<E> + + Matches the generic type List<E>. The type parameter name does not + have to match the name used in the declaration of List, but the bounds must match. + Also matches any parameterization of List that satisfies the bounds of the type variable + (for example, List<String>). + + + + + + + + The *, +, and .. wildcards may be used in type patterns + matching against generic and parameterized types (just as in any other type pattern). The + + wildcard matches all subtypes. Recalling the discussion on subtypes and supertypes in the previous section, note + that the pattern List<Number>+ will match List<Number> and + LinkedList<Number>, but not List<Double>. To match lists of + any number type use the pattern List<Number+> which will match + List<Number>, List<Double>, List<Float> + and so on. + + + + The generics wildcard ? is considered part of the signature of a parameterized type, and + is not used as an AspectJ wildcard in type matching. For example: + + + + + + List<*> + + Matches any parameterized Listtype (List<String>, + List<Integer> and so on). + + + + + + List<?> + + Matches the parameterized type List<?> (and does + not match List<String>, + List<Integer> and so on) + + + + + + List<? extends Number+> + + Matches List<? extends Number>, List<? extends Double>, + and so on, but does not match List<Double>. + + + + + + - + Signature patterns + + + Signature patterns are extended to allow matching of generic methods and constructors, and of + members of generic types and parameterized types. + + + To match members in generic types, the type variables are declared at the start of the + signature pattern as in the following examples: + + + + + <T> T *<T>.* + + Matches a field of the type of type parameter T in any generic type with a single + unbounded type parameter. The field may be of any name. The similar looking pattern <T> T *.* is + not valid as the type parameter T must be bound in the field pattern body. + + + + + + <T extends Number,S> T Util<T,S>.someFunction(List<S>) + + Matches the method someFunction in a generic type Util with + two type parameters, the first type parameter having an upper bound of Number. + + + + + + <E> LinkedList<E>.new() + + Matches the no-argument constructor of the generic type LinkedList. + + + + + + + Matching of members of parameterized types is straightforward. For example, + void List<String>.add(String) matches the add method in the + parameterized type List<String>. + + + To match a generic method simply omit the binding of the type variable(s) in the declaring type + pattern. For example: + + + List *.favourites(List) + ]]> + + matches a generic method favourites declared in any type. To match a + static generic method simply include the static modifier in the type pattern. + + + + Pointcuts + + In this section we discuss how type patterns and signature patterns matching on generic and + parameterized types, methods, and constructors can be used in pointcut expressions. We distinguish + between pointcuts that match based on static type information, and pointcuts that match based on + runtime type information (this, target, args). + + + can have execution jps for parameterized interface types? + + .*(..)) + call(* List.*(..)) + execution(T List.*(..)) + execution(* List.*(T)) + execution( * *(..)) + execution( T *.*(T,T)) + execution( T *.*(T,T)) + execution(static T *.*(T)) + call(* List.*(..)) + call(* List.*(..)) + call(* List.*(..)) + + this/target/args + examples with "+" + ]]> + + declaring pointcuts in generic classes. + + + + + Inter-type Declarations + + + AspectJ 5 allows type parameters to be used in inter-type declarations - either for declaring generic + methods and constructors, or for declaring members on generic types. The syntax for declaring generic + methods and constructors follows the regular AspectJ convention of simply qualifying the member name with + the target type. + + + + + + <T extends Number> T Utils.max(T first, T second) {...} + + Declares a generic instance method max on the class Util. + The max method takes two arguments, first and second which must + both be of the same type (and that type must be Number or a subtype of Number) and returns an instance + of that type. + + + + + + static <E> E Utils.first(List<E> elements) {...} + + Declares a static generic method first on the class Util. + The first method takes a list of elements of some type, and returns an instance + of that type. + + + + + + <T> Sorter.new(List<T> elements,Comparator<? super T> comparator) {...} + + Declares a constructor on the class Sorter. + The constructor takes a list of elements of some type, and a comparator that can compare instances + of the element type. + + + + + + + + A generic type may be the target of an inter-type declaration, used either in its raw form or with + type parameters specified. If type parameters are specified, then the number of type parameters and + their bounds given in the inter-type declararation must be compatible with type parameter definitions in + the generic type declaration. Type parameter names do not have to match. + For example, given the generic type Foo<T,S extends Number> then: + + + + + + String Foo.getName() {...} + + Declares a getName method on behalf of the raw type Foo. It is + not possible to refer to the type parameters of Foo in such a declaration. + + + + + + R Foo<Q, R extends Number>.getMagnitude() {...} + + Declares a method getMagnitude on the generic class Foo. + The method returns an instance of the type substituted for the second type parameter in an invocation + of Foo. + + + + + + R Foo<Q, R>.getMagnitude() {...} + + Results in a compilation error since the generic type Foo with two unbounded + type parameters cannot be found. + + + + + + + A parameterized type may not be the target of an inter-type declaration. This is because + there is only one type (the generic type) regardless of how many different invocations (parameterizations) of + that generic type are made in a program. Therefore it does not make sense to try and declare a member + on behalf of (say) Foo<String>, you can only declare members on the generic + type Foo<T>. + + + + If an inter-type member is declared inside a generic aspect, then the type parameter names from the + aspect declaration may be used in the signature specification of the inter-type declaration, but + not as type parameter names for a generic target type. In other words the example + that follows is legal: + + + { + + private T Foo.data; + + public T Foo.getData(T defaultValue) { + return (this.data != null ? data : defaultValue); + } + + } + ]]> + + + Whereas the following example is not allowed and will report an error that a parameterized type may not be the + target of an inter-type declaration (since when the type parameter T in the aspect is subsituted with + say, String, then the target of the inter-type declaration becomes Goo<String>). + + + { + + private T Goo.data; + + public T Goo.getData(T defaultValue) { + return (this.data != null ? data : defaultValue); + } + + } + ]]> + + + + + Declare Parents + + Both generic and parameterized types can be used as the parent type in a declare parents + statement (as long as the resulting type hierarchy would be well-formed in accordance with Java's sub-typing + rules). Generic types may also be used as the target type of a declare parents statement: + a type variable specification preceeds the type pattern in these cases. + Some examples follow: + + + + + declare parents: Foo implements List<String> + + The Foo type implements the List<String> interface. If + Foo already implements some other parameterization of the List + interface (for example, List<Integer> then a compilation error will result since a + type cannot implement multiple parameterizations of the same generic interface type. + + + + + + declare parents: <T> org.xyz..*<T> extends Base<T> + + All generic types declared in a package beginning with org.xyz and with a + single unbounded type parameter, extend the generic type Base<T>. + + + + + + declare parents: <T> org.xyz..*<T> extends Base<S> + + Results in a compilation error (unless S is a type) since S is + not bound in the type pattern. + + + + + + + + - + Declare Soft + It is an error to use a generic or parameterized type as the softened exception type in a declare soft statement. Java 5 does + not permit a generic class to be a direct or indirect subtype of Throwable (JLS 8.1.2). - + Parameterized Aspects + + + AspectJ 5 allows an abstract aspect to be declared as a generic type. Any concrete + aspect extending a generic abstract aspect must extend a parameterized version of the abstract aspect. + Wildcards are not permitted in this parameterization. + + + Given the aspect declaration: + + { + ... + } + ]]> + + then + + + + + public aspect FilesInFolders extends ParentChildRelationship<Folder,File> {... + + declares a concrete sub-aspect, FilesInFolders which extends the + parameterized abstract aspect ParentChildRelationship<Folder,File>. + + + + + + public aspect FilesInFolders extends ParentChildRelationship {... + + results in a compilation error since the ParentChildRelationship aspect must + be fully parameterized. + + + + + + public aspect ThingsInFolders<T> extends ParentChildRelationship<Folder,T> + + results in a compilation error since concrete aspects may not have type parameters. + + + + + + public abstract aspect ThingsInFolders<T> extends ParentChildRelationship<Folder,T> + + declares a sub-aspect of ParentChildRelationship in which Folder + plays the role of parent (is bound to the type variable P). + + + + + + + An exception to the rule that concrete aspects may not be generic is a pertypewithin aspect, which + may be declared with a single unbounded type parameter. This is discussed in the chapter on . + + The type parameter variables from a generic aspect declaration may be used in place of a type within any + member of the aspect. For example, we can declare a ParentChildRelationship aspect to + manage the bi-directional relationship between parent and child nodes as follows: + + + { + + /** + * Parents contain a list of children + */ + private List P.children; + + /** + * Each child has a parent + */ + private P C.parent; + + /** + * ensure bi-directional navigation on adding a child + */ + public void P.addChild(C child) { + if (child.parent != null) { + child.parent.removeChild(child); + } + children.add(child); + child.parent = this; + } + + /** + * ensure bi-directional navigation on removing a child + */ + public void P.removeChild(C child) { + if (children.remove(child)) { + child.parent = null; + } + } + + /** + * ensure bi-directional navigation on setting parent + */ + public void C.setParent(P parent) { + parent.addChild(this); + } + + public pointcut addingChild(P p, C c) : + execution(* P.addChild(C)) && this(p) && args(c); + + public pointcut removingChild(P p, C c) : + execution(* P.removeChild(C)) && this(p) && args(c); + } + ]]> + + + Note in the above example how the type parameters P and C can be + used in inter-type declarations, pointcut expressions, and any other member of the aspect type. + + + + The example aspect captures the protocol for managing a bi-directional parent-child relationship between + any two types playing the role of parent and child. In a compiler implementation managing an abstract syntax + tree (AST) in which AST nodes may contain other AST nodes we could declare the concrete aspect: + + + { + + before(ASTNode parent, ASTNode child) : addingChild(parent, child) { + ... + } + + } + ]]> + + + As a result of this declaration, ASTNode gains members: + + + + List<ASTNode> children + ASTNode parent + void addChild(ASTNode child) + void removeChild(ASTNode child) + void setParent(ASTNode parent) + + + + In a system managing files and folders, we could declare the concrete aspect: + + + { + + } + ]]> + + + As a result of this declaration, Folder gains members: + + + + List<File> children + void addChild(File child) + void removeChild(File child) + + + and File gains members: + + + Folder parent + void setParent(Folder parent) + + + When used in this way, the type parameters in a generic abstract aspect declare + roles, and the parameterization of the abstract aspect in the extends + clause binds types to those roles. This is a case where you may consider departing from the standard practice + of using a single letter to represent a type parameter, and instead use a role name. It makes no difference + to the compiler which option you choose of course. + + { + + /** + * Parents contain a list of children + */ + private List Parent.children; + + /** + * Each child has a parent + */ + private Parent Child.parent; + + /** + * ensure bi-directional navigation on adding a child + */ + public void Parent.addChild(Child child) { + if (child.parent != null) { + child.parent.removeChild(child); + } + children.add(child); + child.parent = this; + } + + ... + ]]> diff --git a/docs/adk15ProgGuideDB/grammar.xml b/docs/adk15ProgGuideDB/grammar.xml index 0f8320208..1de50b7bf 100644 --- a/docs/adk15ProgGuideDB/grammar.xml +++ b/docs/adk15ProgGuideDB/grammar.xml @@ -9,7 +9,7 @@ '!' TypePattern | '(' AnnotationPattern? TypePattern ')' TypePattern '&&' TypePattern | - TypePattern '||' TypePattern | + TypePattern '||' TypePattern SimpleTypePattern := DottedNamePattern '+'? '[]'*