6298888: Add toGenericString to j.l.Class and getTypeName to j.l.reflect.Type
6992705: Include modifiers in Class.toGenericString() Class.toGenericString and supporting changes; additional reviews by Peter Levart Reviewed-by: alanb
This commit is contained in:
parent
35d38242d2
commit
2c7f3d292f
@ -113,8 +113,7 @@ import sun.reflect.misc.ReflectUtil;
|
||||
* @see java.lang.ClassLoader#defineClass(byte[], int, int)
|
||||
* @since JDK1.0
|
||||
*/
|
||||
public final
|
||||
class Class<T> implements java.io.Serializable,
|
||||
public final class Class<T> implements java.io.Serializable,
|
||||
java.lang.reflect.GenericDeclaration,
|
||||
java.lang.reflect.Type,
|
||||
java.lang.reflect.AnnotatedElement {
|
||||
@ -150,6 +149,75 @@ public final
|
||||
+ getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing this {@code Class}, including
|
||||
* information about modifiers and type parameters.
|
||||
*
|
||||
* The string is formatted as a list of type modifiers, if any,
|
||||
* followed by the kind of type (empty string for primitive types
|
||||
* and {@code class}, {@code enum}, {@code interface}, or {@code
|
||||
* @interface}, as appropriate), followed by the type's name,
|
||||
* followed by an angle-bracketed comma-separated list of the
|
||||
* type's type parameters, if any.
|
||||
*
|
||||
* A space is used to separate modifiers from one another and to
|
||||
* separate any modifiers from the kind of type. The modifiers
|
||||
* occur in canonical order. If there are no type parameters, the
|
||||
* type parameter list is elided.
|
||||
*
|
||||
* <p>Note that since information about the runtime representation
|
||||
* of a type is being generated, modifiers not present on the
|
||||
* originating source code or illegal on the originating source
|
||||
* code may be present.
|
||||
*
|
||||
* @return a string describing this {@code Class}, including
|
||||
* information about modifiers and type parameters
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public String toGenericString() {
|
||||
if (isPrimitive()) {
|
||||
return toString();
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Class modifiers are a superset of interface modifiers
|
||||
int modifiers = getModifiers() & Modifier.classModifiers();
|
||||
if (modifiers != 0) {
|
||||
sb.append(Modifier.toString(modifiers));
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
if (isAnnotation()) {
|
||||
sb.append('@');
|
||||
}
|
||||
if (isInterface()) { // Note: all annotation types are interfaces
|
||||
sb.append("interface");
|
||||
} else {
|
||||
if (isEnum())
|
||||
sb.append("enum");
|
||||
else
|
||||
sb.append("class");
|
||||
}
|
||||
sb.append(' ');
|
||||
sb.append(getName());
|
||||
|
||||
TypeVariable<?>[] typeparms = getTypeParameters();
|
||||
if (typeparms.length > 0) {
|
||||
boolean first = true;
|
||||
sb.append('<');
|
||||
for(TypeVariable<?> typeparm: typeparms) {
|
||||
if (!first)
|
||||
sb.append(',');
|
||||
sb.append(typeparm.getTypeName());
|
||||
first = false;
|
||||
}
|
||||
sb.append('>');
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Class} object associated with the class or
|
||||
@ -1163,6 +1231,32 @@ public final
|
||||
return simpleName.substring(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an informative string for the name of this type.
|
||||
*
|
||||
* @return an informative string for the name of this type
|
||||
* @since 1.8
|
||||
*/
|
||||
public String getTypeName() {
|
||||
if (isArray()) {
|
||||
try {
|
||||
Class<?> cl = this;
|
||||
int dimensions = 0;
|
||||
while (cl.isArray()) {
|
||||
dimensions++;
|
||||
cl = cl.getComponentType();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(cl.getName());
|
||||
for (int i = 0; i < dimensions; i++) {
|
||||
sb.append("[]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Throwable e) { /*FALLTHRU*/ }
|
||||
}
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Character.isDigit answers {@code true} to some non-ascii
|
||||
* digits. This one does not.
|
||||
|
@ -297,7 +297,7 @@ public final class Constructor<T> extends Executable {
|
||||
|
||||
@Override
|
||||
void specificToStringHeader(StringBuilder sb) {
|
||||
sb.append(Field.getTypeName(getDeclaringClass()));
|
||||
sb.append(getDeclaringClass().getTypeName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +82,7 @@ public abstract class Executable extends AccessibleObject
|
||||
|
||||
void separateWithCommas(Class<?>[] types, StringBuilder sb) {
|
||||
for (int j = 0; j < types.length; j++) {
|
||||
sb.append(Field.getTypeName(types[j]));
|
||||
sb.append(types[j].getTypeName());
|
||||
if (j < (types.length - 1))
|
||||
sb.append(",");
|
||||
}
|
||||
@ -161,9 +161,7 @@ public abstract class Executable extends AccessibleObject
|
||||
sb.append('(');
|
||||
Type[] params = getGenericParameterTypes();
|
||||
for (int j = 0; j < params.length; j++) {
|
||||
String param = (params[j] instanceof Class)?
|
||||
Field.getTypeName((Class)params[j]):
|
||||
(params[j].toString());
|
||||
String param = params[j].getTypeName();
|
||||
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
|
||||
param = param.replaceFirst("\\[\\]$", "...");
|
||||
sb.append(param);
|
||||
|
@ -295,8 +295,8 @@ class Field extends AccessibleObject implements Member {
|
||||
public String toString() {
|
||||
int mod = getModifiers();
|
||||
return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
|
||||
+ getTypeName(getType()) + " "
|
||||
+ getTypeName(getDeclaringClass()) + "."
|
||||
+ getType().getTypeName() + " "
|
||||
+ getDeclaringClass().getTypeName() + "."
|
||||
+ getName());
|
||||
}
|
||||
|
||||
@ -324,9 +324,8 @@ class Field extends AccessibleObject implements Member {
|
||||
int mod = getModifiers();
|
||||
Type fieldType = getGenericType();
|
||||
return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
|
||||
+ ((fieldType instanceof Class) ?
|
||||
getTypeName((Class)fieldType): fieldType.toString())+ " "
|
||||
+ getTypeName(getDeclaringClass()) + "."
|
||||
+ fieldType.getTypeName() + " "
|
||||
+ getDeclaringClass().getTypeName() + "."
|
||||
+ getName());
|
||||
}
|
||||
|
||||
@ -996,29 +995,6 @@ class Field extends AccessibleObject implements Member {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility routine to paper over array type names
|
||||
*/
|
||||
static String getTypeName(Class<?> type) {
|
||||
if (type.isArray()) {
|
||||
try {
|
||||
Class<?> cl = type;
|
||||
int dimensions = 0;
|
||||
while (cl.isArray()) {
|
||||
dimensions++;
|
||||
cl = cl.getComponentType();
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(cl.getName());
|
||||
for (int i = 0; i < dimensions; i++) {
|
||||
sb.append("[]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Throwable e) { /*FALLTHRU*/ }
|
||||
}
|
||||
return type.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NullPointerException {@inheritDoc}
|
||||
* @since 1.5
|
||||
|
@ -342,9 +342,8 @@ public final class Method extends Executable {
|
||||
* specified by "The Java Language Specification". This is
|
||||
* {@code public}, {@code protected} or {@code private} first,
|
||||
* and then other modifiers in the following order:
|
||||
* {@code abstract}, {@code static}, {@code final},
|
||||
* {@code synchronized}, {@code native}, {@code strictfp},
|
||||
* {@code default}.
|
||||
* {@code abstract}, {@code default}, {@code static}, {@code final},
|
||||
* {@code synchronized}, {@code native}, {@code strictfp}.
|
||||
*
|
||||
* @return a string describing this {@code Method}
|
||||
*
|
||||
@ -359,8 +358,8 @@ public final class Method extends Executable {
|
||||
|
||||
@Override
|
||||
void specificToStringHeader(StringBuilder sb) {
|
||||
sb.append(Field.getTypeName(getReturnType())).append(' ');
|
||||
sb.append(Field.getTypeName(getDeclaringClass())).append('.');
|
||||
sb.append(getReturnType().getTypeName()).append(' ');
|
||||
sb.append(getDeclaringClass().getTypeName()).append('.');
|
||||
sb.append(getName());
|
||||
}
|
||||
|
||||
@ -387,16 +386,14 @@ public final class Method extends Executable {
|
||||
* class name. If the method is declared to throw exceptions, the
|
||||
* parameter list is followed by a space, followed by the word
|
||||
* throws followed by a comma-separated list of the generic thrown
|
||||
* exception types. If there are no type parameters, the type
|
||||
* parameter list is elided.
|
||||
* exception types.
|
||||
*
|
||||
* <p>The access modifiers are placed in canonical order as
|
||||
* specified by "The Java Language Specification". This is
|
||||
* {@code public}, {@code protected} or {@code private} first,
|
||||
* and then other modifiers in the following order:
|
||||
* {@code abstract}, {@code static}, {@code final},
|
||||
* {@code synchronized}, {@code native}, {@code strictfp},
|
||||
* {@code default}.
|
||||
* {@code abstract}, {@code default}, {@code static}, {@code final},
|
||||
* {@code synchronized}, {@code native}, {@code strictfp}.
|
||||
*
|
||||
* @return a string describing this {@code Method},
|
||||
* include type parameters
|
||||
@ -413,11 +410,8 @@ public final class Method extends Executable {
|
||||
@Override
|
||||
void specificToGenericStringHeader(StringBuilder sb) {
|
||||
Type genRetType = getGenericReturnType();
|
||||
sb.append( ((genRetType instanceof Class<?>)?
|
||||
Field.getTypeName((Class<?>)genRetType):genRetType.toString()))
|
||||
.append(' ');
|
||||
|
||||
sb.append(Field.getTypeName(getDeclaringClass())).append('.');
|
||||
sb.append(genRetType.getTypeName()).append(' ');
|
||||
sb.append(getDeclaringClass().getTypeName()).append('.');
|
||||
sb.append(getName());
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,7 @@ import sun.reflect.ReflectionFactory;
|
||||
* @author Nakul Saraiya
|
||||
* @author Kenneth Russell
|
||||
*/
|
||||
public
|
||||
class Modifier {
|
||||
public class Modifier {
|
||||
|
||||
/*
|
||||
* Bootstrapping protocol between java.lang and java.lang.reflect
|
||||
@ -233,7 +232,7 @@ class Modifier {
|
||||
* represented by {@code mod}
|
||||
*/
|
||||
public static String toString(int mod) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len;
|
||||
|
||||
if ((mod & PUBLIC) != 0) sb.append("public ");
|
||||
@ -393,7 +392,7 @@ class Modifier {
|
||||
*
|
||||
*/
|
||||
static final int ACCESS_MODIFIERS =
|
||||
Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
|
||||
Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
|
||||
|
||||
/**
|
||||
* Return an {@code int} value OR-ing together the source language
|
||||
|
@ -110,21 +110,19 @@ public final class Parameter implements AnnotatedElement {
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Type type = getParameterizedType();
|
||||
final String typename = (type instanceof Class)?
|
||||
Field.getTypeName((Class)type):
|
||||
(type.toString());
|
||||
final String typename = type.getTypeName();
|
||||
|
||||
sb.append(Modifier.toString(getModifiers()));
|
||||
|
||||
if(0 != modifiers)
|
||||
sb.append(" ");
|
||||
sb.append(' ');
|
||||
|
||||
if(isVarArgs())
|
||||
sb.append(typename.replaceFirst("\\[\\]$", "..."));
|
||||
else
|
||||
sb.append(typename);
|
||||
|
||||
sb.append(" ");
|
||||
sb.append(' ');
|
||||
sb.append(getName());
|
||||
|
||||
return sb.toString();
|
||||
|
@ -32,6 +32,17 @@ package java.lang.reflect;
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
|
||||
public interface Type {
|
||||
/**
|
||||
* Returns a string describing this type, including information
|
||||
* about any type parameters.
|
||||
*
|
||||
* @implSpec The default implementation calls {@code toString}.
|
||||
*
|
||||
* @return a string describing this type
|
||||
* @since 1.8
|
||||
*/
|
||||
default String getTypeName() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
90
jdk/test/java/lang/Class/GenericStringTest.java
Normal file
90
jdk/test/java/lang/Class/GenericStringTest.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6298888 6992705
|
||||
* @summary Check Class.toGenericString()
|
||||
* @author Joseph D. Darcy
|
||||
*/
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
@ExpectedGenericString("public class GenericStringTest")
|
||||
public class GenericStringTest {
|
||||
public static void main(String... args){
|
||||
int failures = 0;
|
||||
|
||||
failures += checkToGenericString(int.class, "int");
|
||||
|
||||
Class<?>[] types = {
|
||||
GenericStringTest.class,
|
||||
AnInterface.class,
|
||||
LocalMap.class,
|
||||
AnEnum.class,
|
||||
AnotherEnum.class,
|
||||
};
|
||||
|
||||
for(Class<?> clazz : types) {
|
||||
failures += checkToGenericString(clazz, clazz.getAnnotation(ExpectedGenericString.class).value());
|
||||
}
|
||||
|
||||
if (failures > 0) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static int checkToGenericString(Class<?> clazz, String expected) {
|
||||
String genericString = clazz.toGenericString();
|
||||
if (!genericString.equals(expected)) {
|
||||
System.err.printf("Unexpected Class.toGenericString output; expected '%s', got '%s'.%n",
|
||||
expected,
|
||||
genericString);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ExpectedGenericString {
|
||||
String value();
|
||||
}
|
||||
|
||||
@ExpectedGenericString("abstract interface AnInterface")
|
||||
strictfp interface AnInterface {}
|
||||
|
||||
@ExpectedGenericString("abstract interface LocalMap<K,V>")
|
||||
interface LocalMap<K,V> {}
|
||||
|
||||
@ExpectedGenericString("final enum AnEnum")
|
||||
enum AnEnum {
|
||||
FOO;
|
||||
}
|
||||
|
||||
@ExpectedGenericString("enum AnotherEnum")
|
||||
enum AnotherEnum {
|
||||
BAR{};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user