--- old/src/java.base/share/classes/java/lang/Class.java 2018-10-11 17:46:31.940000000 -0700 +++ new/src/java.base/share/classes/java/lang/Class.java 2018-10-11 17:46:31.756000000 -0700 @@ -200,7 +200,8 @@ * 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. + * comma-separated list of the type's type parameters, if any, + * including informative bounds on the 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 @@ -264,7 +265,7 @@ if (typeparms.length > 0) { StringJoiner sj = new StringJoiner(",", "<", ">"); for(TypeVariable typeparm: typeparms) { - sj.add(typeparm.getTypeName()); + sj.add(typeVarBounds(typeparm)); } sb.append(sj.toString()); } @@ -276,6 +277,19 @@ } } + String typeVarBounds(TypeVariable typeVar) { + Type[] bounds = typeVar.getBounds(); + if (bounds.length == 1 && bounds[0].equals(Object.class)) { + return typeVar.getName(); + } else { + StringJoiner sj = new StringJoiner(" & "); + for (Type bound : bounds) { + sj.add(bound.getTypeName()); + } + return typeVar.getName() + " extends " + sj.toString(); + } + } + /** * Returns the {@code Class} object associated with the class or * interface with the given string name. Invoking this method is