From 19691fab48657174a4e9768d33da2fdd532271c9 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Fri, 14 Jul 2023 20:18:23 +0000 Subject: [PATCH] 6361826: (reflect) provide method for mapping strings to class object for primitive types Reviewed-by: rriggs, mchung --- .../classes/java/io/ObjectInputStream.java | 17 +-- .../share/classes/java/lang/Class.java | 38 +++++++ .../jdk/java/lang/Class/ForPrimitiveName.java | 103 ++++++++++++++++++ 3 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 test/jdk/java/lang/Class/ForPrimitiveName.java diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index 10b7f7715fa..a803969df5a 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -261,21 +261,6 @@ public class ObjectInputStream /** marker for unshared objects in internal handle table */ private static final Object unsharedMarker = new Object(); - /** - * immutable table mapping primitive type names to corresponding - * class objects - */ - private static final Map> primClasses = - Map.of("boolean", boolean.class, - "byte", byte.class, - "char", char.class, - "short", short.class, - "int", int.class, - "long", long.class, - "float", float.class, - "double", double.class, - "void", void.class); - private static class Caches { /** cache of subclass security audit results */ static final ClassValue subclassAudits = @@ -803,7 +788,7 @@ public class ObjectInputStream try { return Class.forName(name, false, latestUserDefinedLoader()); } catch (ClassNotFoundException ex) { - Class cl = primClasses.get(name); + Class cl = Class.forPrimitiveName(name); if (cl != null) { return cl; } else { diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index a94e1aa9fde..bfbe4d9ae6c 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -438,6 +438,9 @@ public final class Class implements java.io.Serializable, * 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. + * To obtain a {@code Class} object for a named primitive type + * such as {@code int} or {@code long} use {@link + * #forPrimitiveName(String)}. * *

To obtain the {@code Class} object associated with an array class, * the name consists of one or more {@code '['} representing the depth @@ -628,6 +631,41 @@ public final class Class implements java.io.Serializable, } } + /** + * {@return the {@code Class} object associated with the + * {@linkplain #isPrimitive() primitive type} of the given name} + * If the argument is not the name of a primitive type, {@code + * null} is returned. + * + * @param primitiveName the name of the primitive type to find + * + * @throws NullPointerException if the argument is {@code null} + * + * @jls 4.2 Primitive Types and Values + * @jls 15.8.2 Class Literals + * @since 22 + */ + public static Class forPrimitiveName(String primitiveName) { + return switch(primitiveName) { + // Integral types + case "int" -> int.class; + case "long" -> long.class; + case "short" -> short.class; + case "char" -> char.class; + case "byte" -> byte.class; + + // Floating-point types + case "float" -> float.class; + case "double" -> double.class; + + // Other types + case "boolean" -> boolean.class; + case "void" -> void.class; + + default -> null; + }; + } + /** * Creates a new instance of the class represented by this {@code Class} * object. The class is instantiated as if by a {@code new} diff --git a/test/jdk/java/lang/Class/ForPrimitiveName.java b/test/jdk/java/lang/Class/ForPrimitiveName.java new file mode 100644 index 00000000000..e6e9f5075c0 --- /dev/null +++ b/test/jdk/java/lang/Class/ForPrimitiveName.java @@ -0,0 +1,103 @@ +/* + * 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 + * @summary Test Class.forPrimitiveName + * @bug 6361826 + */ + +import java.util.*; + +public class ForPrimitiveName { + public static void main(String... args) { + positiveTests(); + negativeTests(); + } + + private static final void positiveTests() { + + /** + * immutable table mapping primitive type names to corresponding + * class objects + */ + final Map> primClasses = + Map.of("boolean", boolean.class, + "byte", byte.class, + "char", char.class, + "short", short.class, + "int", int.class, + "long", long.class, + "float", float.class, + "double", double.class, + "void", void.class); + + for (var entry : primClasses.entrySet()) { + String key = entry.getKey(); + Class expected = entry.getValue(); + Class result = Class.forPrimitiveName(key); + + // For java.lang.Class, equality is identity. + if (result != expected) { + throw new RuntimeException("Unexpected mapping for " + key); + } + } + } + + private static final void negativeTests() { + final List expectedNull = + List.of("java.lang.Object", + "java.lang.String", + + // descriptor string names for primitive types + "Z", // boolean + "B", // byte + "C", // char + "D", // double + "F", // float + "I", // int + "L", // long + "S", // short + "V", // void + + // Wrapper classes + "java.lang.Byte", + "java.lang.Boolean", + "java.lang.Character", + "java.lang.Short", + "java.lang.Integer", + "java.lang.Long", + "java.lang.Float", + "java.lang.Double", + "java.lang.Void"); + for (var entry : expectedNull) { + Class result = Class.forPrimitiveName(entry); + if (result != null ) { + throw new RuntimeException("Unexpected nonnull result for " + + entry); + } + } + } +} + +