Skip to main content

java generic

· 7 min read

java的泛型

java的泛型是什么?
泛型是class/interface/method/constructor的一个属性,简单来说是一个修饰符,所以当我们说java的泛型的时候,需要描述4个内容:class/interface/method/constructor

java 泛型

术语表:

名称翻译(我自己的翻译)定义来源举例
parameterized type参数化类型A parameterized type is a class or interface type of the form C<T1,...,Tn>, where C is the name of a generic class or interface, and <T1,...,Tn> is a list of type arguments that denote a particular parameterization of the generic class or interface.4.5List<String>
TypeIdentifier类型标识符TypeIdentifier is used in the declaration of classes, interfaces,and type parameters (§8.1,§9.1, §4.4), and when referring to types (§6.5)--
type variable类型变量A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies. A type variable is introduced by the declaration of a type parameter of a generic class, interface, method, or constructor (§8.1.2, §9.1.2, §8.4.4, §8.8.4)$4.4--

类型参数和类型变量

两者的关系:
泛型参数由很多东西组成,其中泛型参数可以由泛型变量组成.

类型参数(type parameter)

类型参数可以由类型变量构成

类变量(type variable)

类型变量加上一些其他标识符(identifier)可以组成类型参数

什么是类型变量(type variable):

A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.

类型变量是作用在类/接口/方法/构造函数的标识符(identifier)

所以得出结论: 类型变量是标识符

类型变量的作用

A type variable is introduced by the declaration of a type parameter of a generic class, interface, method, or constructor

类型参数的作用域(type parameter scope)

分为两部分: 作用在类/接口是一类,另外一类是作用在构造函数/方法的

作用在类上的:

The scope of a class's type parameter (§8.1.2) is the type parameter section of the class declaration, and the type parameter section of any superclass type or superinterface type of the class declaration, and the class body. If the class is a NAMES Scope of a Declaration 6.3 record class (§8.10), then the scope of the type parameter additionally includes the header of the record declaration (§8.10.1).

泛型类、接口、方法、构造函数

关于泛型相关内容,分为四种:

  • 泛型类(generic class)
  • 泛型接口(generic interface)
  • 泛型方法(generic method)
  • 泛型构造函数(generic constructor)

泛型类:

A class is generic if the class declaration declares one or more type variables

泛型构造函数:

A constructor is generic if it declares one or more type variable

泛型接口:

An interface is generic if the interface declaration declares one or more type variables

泛型构造函数:

A constructor is generic if it declares one or more type variables

类型实参 TypeArguments

类型描述符 TypeIdentifier

区别

泛型接口和泛型类是差不多,泛型构造函数和泛型方法是差不多.
所以我们只要区分泛型类和泛型方法的区别就行

实现

java 的实现,我们可以直接看javac的源码

    /**
* {@literal
* TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"]
* }
*/
protected List<JCTypeParameter> typeParametersOpt() {
return typeParametersOpt(false);
}

/**
* {@literal
* TypeParameter = [Annotations] TypeVariable [TypeParameterBound]
* TypeParameterBound = EXTENDS Type {"&" Type}
* TypeVariable = Ident
* }
*/
JCTypeParameter typeParameter() {
int pos = token.pos;
List<JCAnnotation> annos = typeAnnotationsOpt();
Name name = typeName();
ListBuffer<JCExpression> bounds = new ListBuffer<>();
if (token.kind == EXTENDS) {
nextToken();
bounds.append(parseType());
while (token.kind == AMP) {
nextToken();
bounds.append(parseType());
}
}
return toP(F.at(pos).TypeParameter(name, bounds.toList(), annos));
}
    Name typeName() {
int pos = token.pos;
Name name = ident();
Source source = restrictedTypeNameStartingAtSource(name, pos, true);
if (source != null) {
reportSyntaxError(pos, Errors.RestrictedTypeNotAllowed(name, source));
}
return name;
}
    protected Name ident(boolean allowClass) {
if (token.kind == IDENTIFIER) {
Name name = token.name();
nextToken();
return name;
} else if (token.kind == ASSERT) {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.AssertAsIdentifier);
nextToken();
return names.error;
} else if (token.kind == ENUM) {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.EnumAsIdentifier);
nextToken();
return names.error;
} else if (token.kind == THIS) {
if (allowThisIdent) {
Name name = token.name();
nextToken();
return name;
} else {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.ThisAsIdentifier);
nextToken();
return names.error;
}
} else if (token.kind == UNDERSCORE) {
if (Feature.UNDERSCORE_IDENTIFIER.allowedInSource(source)) {
log.warning(token.pos, Warnings.UnderscoreAsIdentifier);
} else {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UnderscoreAsIdentifier);
}
Name name = token.name();
nextToken();
return name;
} else {
accept(IDENTIFIER);
if (allowClass && token.kind == CLASS) {
nextToken();
return names._class;
}
return names.error;
}
}
main[1] stop in  com.sun.tools.javac.parser.Tokens$Token:315 
Deferring breakpoint com.sun.tools.javac.parser.Tokens$Token:315.
It will be set after the class is loaded.
main[1] cont
> Set deferred breakpoint com.sun.tools.javac.parser.Tokens$Token:315

Breakpoint hit: "thread=main", com.sun.tools.javac.parser.Tokens$Token.<init>(), line=315 bci=14
315 this.endPos = endPos;

main[1] list
311
312 Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
313 this.kind = kind;
314 this.pos = pos;
315 => this.endPos = endPos;
316 this.comments = comments;
317 checkKind();
318 }
319
320 Token[] split(Tokens tokens) {
main[1] where
[1] com.sun.tools.javac.parser.Tokens$Token.<init> (Tokens.java:315)
[2] com.sun.tools.javac.parser.Tokens.<clinit> (Tokens.java:457)
[3] com.sun.tools.javac.parser.ParserFactory.<init> (ParserFactory.java:79)
[4] com.sun.tools.javac.parser.ParserFactory.instance (ParserFactory.java:56)
[5] com.sun.tools.javac.main.JavaCompiler.<init> (JavaCompiler.java:386)
[6] com.sun.tools.javac.main.JavaCompiler.instance (JavaCompiler.java:115)
[7] com.sun.tools.javac.processing.JavacProcessingEnvironment.<init> (JavacProcessingEnvironment.java:215)
[8] com.sun.tools.javac.processing.JavacProcessingEnvironment.instance (JavacProcessingEnvironment.java:200)
[9] com.sun.tools.javac.api.BasicJavacTask.initPlugins (BasicJavacTask.java:217)
[10] com.sun.tools.javac.main.Main.compile (Main.java:292)
[11] com.sun.tools.javac.main.Main.compile (Main.java:176)
[12] com.sun.tools.javac.Main.compile (Main.java:64)
[13] com.sun.tools.javac.Main.main (Main.java:50)

如何调试

窗口1:

##  用javac 编译Hello.java
./java -agentlib:jdwp=transport=dt_socket,server=y,address=8000 --module jdk.compiler/com.sun.tools.javac.Main com/Hello.java

窗口2:

### jdb断点
./jdb -attach 8000 -sourcepath /var/jdk/src/jdk.compiler/share/classes
### 在jdb中 断点main函数
stop in com.sun.tools.javac.Main.main
### 继续执行 命令是cont , 也可以是continue
continue

泛型方法的代码

class PARA{
<TT> void test(TT para ){
System.out.println(para);
}
}

满足了这个parseRule

 ClassBodyDeclaration =
";"
| [STATIC] Block
| ModifiersOpt
( Type Ident
( VariableDeclaratorsRest ";" | MethodDeclaratorRest )
| VOID Ident VoidMethodDeclaratorRest
| TypeParameters [Annotations]
( Type Ident MethodDeclaratorRest
| VOID Ident VoidMethodDeclaratorRest
)
| Ident ConstructorDeclaratorRest
| TypeParameters Ident ConstructorDeclaratorRest
| ClassOrInterfaceOrEnumDeclaration
* )

堆栈

Breakpoint hit: "thread=main", com.sun.tools.javac.parser.JavacParser.ident(), line=575 bci=0
575 if (token.kind == IDENTIFIER) {

main[1] print token.name()
token.name() = "TT"
main[1] where
[1] com.sun.tools.javac.parser.JavacParser.ident (JavacParser.java:575)
[2] com.sun.tools.javac.parser.JavacParser.ident (JavacParser.java:571)
[3] com.sun.tools.javac.parser.JavacParser.typeName (JavacParser.java:3,979)
[4] com.sun.tools.javac.parser.JavacParser.typeParameter (JavacParser.java:4,563)
[5] com.sun.tools.javac.parser.JavacParser.typeParametersOpt (JavacParser.java:4,541)
[6] com.sun.tools.javac.parser.JavacParser.classOrInterfaceOrRecordBodyDeclaration (JavacParser.java:4,277)
[7] com.sun.tools.javac.parser.JavacParser.classInterfaceOrRecordBody (JavacParser.java:4,214)
[8] com.sun.tools.javac.parser.JavacParser.classDeclaration (JavacParser.java:3,925)
[9] com.sun.tools.javac.parser.JavacParser.classOrRecordOrInterfaceOrEnumDeclaration (JavacParser.java:3,866)
[10] com.sun.tools.javac.parser.JavacParser.typeDeclaration (JavacParser.java:3,855)
[11] com.sun.tools.javac.parser.JavacParser.parseCompilationUnit (JavacParser.java:3,699)
[12] com.sun.tools.javac.main.JavaCompiler.parse (JavaCompiler.java:620)
[13] com.sun.tools.javac.main.JavaCompiler.parse (JavaCompiler.java:657)
[14] com.sun.tools.javac.main.JavaCompiler.parseFiles (JavaCompiler.java:1,006)
[15] com.sun.tools.javac.main.JavaCompiler.parseFiles (JavaCompiler.java:993)
[16] com.sun.tools.javac.main.JavaCompiler.compile (JavaCompiler.java:919)
[17] com.sun.tools.javac.main.Main.compile (Main.java:317)
[18] com.sun.tools.javac.main.Main.compile (Main.java:176)
[19] com.sun.tools.javac.Main.compile (Main.java:64)
[20] com.sun.tools.javac.Main.main (Main.java:50)
main[1] print kind 
kind = "token.identifier"
main[1] print pos
pos = 13
main[1] list
311
312 Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
313 this.kind = kind;
314 this.pos = pos;
315 => this.endPos = endPos;
316 this.comments = comments;
317 checkKind();
318 }
319
320 Token[] split(Tokens tokens) {
main[1] where
[1] com.sun.tools.javac.parser.Tokens$Token.<init> (Tokens.java:315)
[2] com.sun.tools.javac.parser.Tokens$NamedToken.<init> (Tokens.java:399)
[3] com.sun.tools.javac.parser.JavaTokenizer.readToken (JavaTokenizer.java:1,046)
[4] com.sun.tools.javac.parser.Scanner.nextToken (Scanner.java:115)
[5] com.sun.tools.javac.parser.JavacParser.nextToken (JavacParser.java:275)
[6] com.sun.tools.javac.parser.JavacParser.typeParametersOpt (JavacParser.java:4,540)
[7] com.sun.tools.javac.parser.JavacParser.classOrInterfaceOrRecordBodyDeclaration (JavacParser.java:4,277)
[8] com.sun.tools.javac.parser.JavacParser.classInterfaceOrRecordBody (JavacParser.java:4,214)
[9] com.sun.tools.javac.parser.JavacParser.classDeclaration (JavacParser.java:3,925)
[10] com.sun.tools.javac.parser.JavacParser.classOrRecordOrInterfaceOrEnumDeclaration (JavacParser.java:3,866)
[11] com.sun.tools.javac.parser.JavacParser.typeDeclaration (JavacParser.java:3,855)
[12] com.sun.tools.javac.parser.JavacParser.parseCompilationUnit (JavacParser.java:3,699)
[13] com.sun.tools.javac.main.JavaCompiler.parse (JavaCompiler.java:620)
[14] com.sun.tools.javac.main.JavaCompiler.parse (JavaCompiler.java:657)
[15] com.sun.tools.javac.main.JavaCompiler.parseFiles (JavaCompiler.java:1,006)
[16] com.sun.tools.javac.main.JavaCompiler.parseFiles (JavaCompiler.java:993)
[17] com.sun.tools.javac.main.JavaCompiler.compile (JavaCompiler.java:919)
[18] com.sun.tools.javac.main.Main.compile (Main.java:317)
[19] com.sun.tools.javac.main.Main.compile (Main.java:176)
[20] com.sun.tools.javac.Main.compile (Main.java:64)
[21] com.sun.tools.javac.Main.main (Main.java:50)

相关阅读