8310242: Clarify the name parameter to Class::forName

Reviewed-by: rriggs, liach, alanb, dholmes
This commit is contained in:
Mandy Chung 2023-06-26 19:56:24 +00:00
parent 297c799631
commit 7db2f08756
2 changed files with 138 additions and 32 deletions
src/java.base/share/classes/java/lang
test/jdk/java/lang/Class/forName

@ -377,7 +377,7 @@ public final class Class<T> implements java.io.Serializable,
* the current class.
*
* <p> For example, the following code fragment returns the
* runtime {@code Class} descriptor for the class named
* runtime {@code Class} object for the class named
* {@code java.lang.Thread}:
*
* {@snippet lang="java" :
@ -392,9 +392,10 @@ public final class Class<T> implements java.io.Serializable,
* caller frame on the stack (e.g. when called directly from a JNI
* attached thread), the system class loader is used.
*
* @param className the fully qualified name of the desired class.
* @return the {@code Class} object for the class with the
* specified name.
* @param className the {@linkplain ClassLoader##binary-name binary name}
* of the class or the string representing an array type
* @return the {@code Class} object for the class with the
* specified name.
* @throws LinkageError if the linkage fails
* @throws ExceptionInInitializerError if the initialization provoked
* by this method fails
@ -423,45 +424,52 @@ public final class Class<T> implements java.io.Serializable,
/**
* Returns the {@code Class} object associated with the class or
* interface with the given string name, using the given class loader.
* Given the fully qualified name for a class or interface (in the same
* format returned by {@code getName}) this method attempts to
* locate and load the class or interface. The specified class
* loader is used to load the class or interface. If the parameter
* {@code loader} is null, the class is loaded through the bootstrap
* Given the {@linkplain ClassLoader##binary-name binary name} for a class or interface,
* this method attempts to locate and load the class or interface. The specified
* class loader is used to load the class or interface. If the parameter
* {@code loader} is {@code null}, the class is loaded through the bootstrap
* class loader. The class is initialized only if the
* {@code initialize} parameter is {@code true} and if it has
* not been initialized earlier.
*
* <p> If {@code name} denotes a primitive type or void, an attempt
* will be made to locate a user-defined class in the unnamed package whose
* name is {@code name}. Therefore, this method cannot be used to
* obtain any of the {@code Class} objects representing primitive
* types or void.
* <p> This method cannot be used to obtain any of the {@code Class} objects
* representing primitive types or void, hidden classes or interfaces,
* or array classes whose element type is a hidden class or interface.
* If {@code name} denotes a primitive type or void, for example {@code I},
* an attempt will be made to locate a user-defined class in the unnamed package
* whose name is {@code I} instead.
*
* <p> If {@code name} denotes an array class, the component type of
* the array class is loaded but not initialized.
*
* <p> For example, in an instance method the expression:
* <p> To obtain the {@code Class} object associated with an array class,
* the name consists of one or more {@code '['} representing the depth
* of the array nesting, followed by the element type as encoded in
* {@linkplain ##nameFormat the table} specified in {@code Class.getName()}.
*
* <p> Examples:
* {@snippet lang="java" :
* Class.forName("Foo")
* Class<?> threadClass = Class.forName("java.lang.Thread", false, currentLoader);
* Class<?> stringArrayClass = Class.forName("[Ljava.lang.String;", false, currentLoader);
* Class<?> intArrayClass = Class.forName("[[[I", false, currentLoader); // Class of int[][][]
* Class<?> nestedClass = Class.forName("java.lang.Character$UnicodeBlock", false, currentLoader);
* Class<?> fooClass = Class.forName("Foo", true, currentLoader);
* }
*
* is equivalent to:
* <p> A call to {@code getName()} on the {@code Class} object returned
* from {@code forName(}<i>N</i>{@code )} returns <i>N</i>.
*
* {@snippet lang="java" :
* Class.forName("Foo", true, this.getClass().getClassLoader())
* }
* <p> A call to {@code forName("[L}<i>N</i>{@code ;")} causes the element type
* named <i>N</i> to be loaded but not initialized regardless of the value
* of the {@code initialize} parameter.
*
* Note that this method throws errors related to loading, linking
* or initializing as specified in Sections {@jls 12.2}, {@jls
* 12.3}, and {@jls 12.4} of <cite>The Java Language
* Specification</cite>.
* Note that this method does not check whether the requested class
* @apiNote
* This method throws errors related to loading, linking or initializing
* as specified in Sections {@jls 12.2}, {@jls 12.3}, and {@jls 12.4} of
* <cite>The Java Language Specification</cite>.
* In addition, this method does not check whether the requested class
* is accessible to its caller.
*
* @param name fully qualified name of the desired class
* @param name the {@linkplain ClassLoader##binary-name binary name}
* of the class or the string representing an array class
*
* @param initialize if {@code true} the class will be initialized
* (which implies linking). See Section {@jls
* 12.4} of <cite>The Java Language
@ -486,6 +494,7 @@ public final class Class<T> implements java.io.Serializable,
* @jls 12.2 Loading of Classes and Interfaces
* @jls 12.3 Linking of Classes and Interfaces
* @jls 12.4 Initialization of Classes and Interfaces
* @jls 13.1 The Form of a Binary
* @since 1.2
*/
@CallerSensitive
@ -548,7 +557,9 @@ public final class Class<T> implements java.io.Serializable,
* accessible to its caller. </p>
*
* @apiNote
* This method returns {@code null} on failure rather than
* This method does not support loading of array types, unlike
* {@link #forName(String, boolean, ClassLoader)}. The class name must be
* a binary name. This method returns {@code null} on failure rather than
* throwing a {@link ClassNotFoundException}, as is done by
* the {@link #forName(String, boolean, ClassLoader)} method.
* The security check is a stack-based permission check if the caller
@ -898,7 +909,7 @@ public final class Class<T> implements java.io.Serializable,
* representing the depth of the array nesting, followed by the element
* type as encoded using the following table:
*
* <blockquote><table class="striped">
* <blockquote><table class="striped" id="nameFormat">
* <caption style="display:none">Element types and encodings</caption>
* <thead>
* <tr><th scope="col"> Element Type <th scope="col"> Encoding
@ -925,6 +936,8 @@ public final class Class<T> implements java.io.Serializable,
* <blockquote><pre>
* String.class.getName()
* returns "java.lang.String"
* Character.UnicodeBlock.class.getName()
* returns "java.lang.Character$UnicodeBlock"
* byte.class.getName()
* returns "byte"
* (new Object[3]).getClass().getName()

@ -0,0 +1,93 @@
/*
* Copyright (c) 2023, 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 8310242
* @run junit ForNameNames
* @summary Verify class names for Class.forName
*/
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class ForNameNames {
static class Inner {}
static Stream<Arguments> testCases() {
return Stream.of(
Arguments.of("java.lang.String", String.class),
Arguments.of("[Ljava.lang.String;", String[].class),
Arguments.of("ForNameNames$Inner", Inner.class),
Arguments.of("[LForNameNames$Inner;", Inner.class),
Arguments.of("[[I", int[][].class)
);
}
/*
* Test 1-arg and 3-arg Class::forName. Class::getName on the returned
* Class object returns the name passed to Class::forName.
*/
@ParameterizedTest
@MethodSource("testCases")
void testForName(String cn, Class<?> expected) throws ClassNotFoundException {
ClassLoader loader = ForNameNames.class.getClassLoader();
Class<?> c1 = Class.forName(cn, false, loader);
assertEquals(expected, c1);
assertEquals(cn, c1.getName());
Class<?> c2 = Class.forName(cn);
assertEquals(expected, c2);
assertEquals(cn, c2.getName());
}
static Stream<Arguments> invalidNames() {
return Stream.of(
Arguments.of("I"), // primitive type
Arguments.of("int[]"), // fully-qualified name of int array
Arguments.of("ForNameNames.Inner"), // fully-qualified name of nested type
Arguments.of("[java.lang.String"), // missing L and ;
Arguments.of("[Ljava.lang.String"), // missing ;
Arguments.of("[Ljava/lang/String;") // type descriptor
);
}
@ParameterizedTest
@MethodSource("invalidNames")
void testInvalidNames(String cn) {
ClassLoader loader = ForNameNames.class.getClassLoader();
assertThrows(ClassNotFoundException.class, () -> Class.forName(cn, false, loader));
}
@Test
void testModule() {
// Class.forName(Module, String) does not allow class name for array types
Class<?> c = Class.forName(Object.class.getModule(), "[Ljava.lang.String;");
assertNull(c);
}
}