Skip to content

8346307: [lworld] Clarify identity vs value in Class, Objects, and document limitations of value objects #1327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: lworld
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions src/java.base/share/classes/java/lang/Class.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,18 +333,20 @@ public String toGenericString() {
if (isAnnotation()) {
sb.append('@');
}
if (isValue()) {
sb.append("value ");
}
if (isInterface()) { // Note: all annotation interfaces are interfaces
sb.append("interface");
} else {
if (isEnum())
sb.append("enum");
else if (isRecord())
sb.append("record");
else
sb.append("class");
else {
if (isValue()) {
sb.append("value ");
}
if (isRecord())
sb.append("record");
else
sb.append("class");
}
}
sb.append(' ');
sb.append(getName());
Expand Down Expand Up @@ -611,36 +613,34 @@ public static Class<?> forName(Module module, String name) {
}

/**
* {@return {@code true} if this {@code Class} object represents an identity
* class or interface; otherwise {@code false}}
* {@return {@code true} if this {@code Class} object represents an identity class;
* otherwise {@code false}}
*
* If this {@code Class} object represents an array type, then this method
* returns {@code true}.
* If this {@code Class} object represents a primitive type, or {@code void},
* then this method returns {@code false}.
* If this {@code Class} object represents an array type then this method returns {@code true}.
* If this {@code Class} object represents an interface, a primitive type, or {@code void}
* this method returns {@code false}.
*
* @see AccessFlag#IDENTITY
* @since Valhalla
*/
@PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
public native boolean isIdentity();

/**
* {@return {@code true} if this {@code Class} object represents a value
* class; otherwise {@code false}}
* {@return {@code true} if this {@code Class} object represents a value class;
* otherwise {@code false}}
* All classes that are not {@linkplain #isIdentity identity classes} are value classes.
*
* If this {@code Class} object represents an array type, an interface,
* a primitive type, or {@code void}, then this method returns {@code false}.
* If this {@code Class} object represents an array type then this method returns {@code false}.
* If this {@code Class} object represents an interface, a primitive type, or {@code void}
* this method returns {@code true}.
*
* @see AccessFlag#IDENTITY
* @since Valhalla
*/
@PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
public boolean isValue() {
if (!PreviewFeatures.isEnabled()) {
return false;
}
if (isPrimitive() || isArray() || isInterface())
return false;
return ((getModifiers() & Modifier.IDENTITY) == 0);
return PreviewFeatures.isEnabled() ? !isIdentity() : false;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/java.base/share/classes/java/lang/IdentityException.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
* <p>
* Identity objects are required for synchronization and locking.
* <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">Value-based</a>
* objects do not have identity and cannot be used for synchronization or locking.
* objects do not have identity and cannot be used for synchronization, locking,
* or any type of {@link java.lang.ref.Reference}.
*
* @since Valhalla
*/
Expand Down
13 changes: 10 additions & 3 deletions src/java.base/share/classes/java/lang/Object.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
* <p>
* Subclasses of {@code java.lang.Object} can be either an {@linkplain Class#isIdentity identity class}
* or a {@linkplain Class#isValue value class}.
* See {@jls The Java Language Specification 8.1.1.5 Value Classes}.
* <div class="preview-block">
* <div class="preview-comment">
* When preview features are enabled, subclasses of {@code java.lang.Object} can be either
* an {@linkplain Class#isIdentity identity class} or a {@linkplain Class#isValue value class}.
* See {@jls The Java Language Specification 8.1.1.5 Value Classes}.
* Use of value class instances for synchronization, mutexes, or with
* {@linkplain java.lang.ref.Reference object references} result in
* {@link IdentityException}.
* </div>
* </div>
*
* @see java.lang.Class
* @since 1.0
Expand Down
15 changes: 15 additions & 0 deletions src/java.base/share/classes/java/lang/System.java
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,21 @@ public static native void arraycopy(Object src, int srcPos,
* hashCode().
* The hash code for the null reference is zero.
*
* <div class="preview-block">
* <div class="preview-comment">
* The "identity hash code" of a {@linkplain Class#isValue() value object}
* is computed by combining the identity hash codes of the value object's fields recursively.
* </div>
* </div>
* @apiNote
* <div class="preview-block">
* <div class="preview-comment">
* Note that, like ==, this hash code exposes information about a value object's
* private fields that might otherwise be hidden by an identity object.
* Developers should be cautious about storing sensitive secrets in value object fields.
* </div>
* </div>
*
* @param x object for which the hashCode is to be calculated
* @return the hashCode
* @since 1.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
Expand Down Expand Up @@ -596,7 +596,7 @@ LoadableDescriptorsAttributeBuilder add(MethodType... mtypes) {
}

boolean requiresLoadableDescriptors(Class<?> cls) {
return cls.isValue() && cls.accessFlags().contains(AccessFlag.FINAL);
return cls.isValue() && cls.accessFlags().contains(AccessFlag.FINAL) && !cls.isPrimitive();
}

boolean isEmpty() {
Expand Down
6 changes: 3 additions & 3 deletions src/java.base/share/classes/java/util/IdentityHashMap.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2025, 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
Expand Down Expand Up @@ -35,8 +35,8 @@

/**
* This class implements the {@code Map} interface with a hash table, using
* reference-equality in place of object-equality when comparing keys (and
* values). In other words, in an {@code IdentityHashMap}, two keys
* `==` in place of object-equality when comparing keys (and values).
* In other words, in an {@code IdentityHashMap}, two keys
* {@code k1} and {@code k2} are considered equal if and only if
* {@code (k1==k2)}. (In normal {@code Map} implementations (like
* {@code HashMap}) two keys {@code k1} and {@code k2} are considered equal
Expand Down
36 changes: 27 additions & 9 deletions src/java.base/share/classes/java/util/Objects.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2025, 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
Expand Down Expand Up @@ -28,7 +28,6 @@
import jdk.internal.javac.PreviewFeature;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.misc.Unsafe;

import java.util.function.Supplier;

Expand Down Expand Up @@ -180,19 +179,38 @@ public static String toIdentityString(Object o) {
}

/**
* {@return {@code true} if the specified object reference is an identity object,
* otherwise {@code false}}
* {@return {@code true} if the object is a non-null reference
* to an {@linkplain Class#isIdentity() identity object}, otherwise {@code false}}
*
* @param obj an object
* @throws NullPointerException if {@code obj} is {@code null}
* @apiNote
* If the parameter is {@code null}, there is no object
* and hence no class to check for identity; the return is {@code false}.
* To test for a {@linkplain Class#isValue() value object} use:
* {@snippet type="java" :
* if (obj != null && !Objects.hasIdentity(obj)) {
* // obj is a non-null value object
* }
* }
* @param obj an object or {@code null}
* @since Valhalla
*/
@PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
// @IntrinsicCandidate
public static boolean hasIdentity(Object obj) {
requireNonNull(obj);
return obj.getClass().isIdentity() || // Before Valhalla all classes are identity classes
obj.getClass() == Object.class;
return (obj == null) ? false : obj.getClass().isIdentity();
}

/**
* {@return {@code true} if the object is a non-null reference
* to an {@linkplain Class#isValue() value object}, otherwise {@code false}}
*
* @param obj an object or {@code null}
* @since Valhalla
*/
@PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
// @IntrinsicCandidate
public static boolean isValueObject(Object obj) {
return (obj == null) ? false : obj.getClass().isValue();
}

/**
Expand Down
25 changes: 23 additions & 2 deletions src/java.base/share/classes/java/util/WeakHashMap.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2025, 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
Expand All @@ -25,8 +25,8 @@

package java.util;

import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
Expand Down Expand Up @@ -122,6 +122,22 @@
* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
* Java Collections Framework</a>.
*
* @apiNote
* <div class="preview-block">
* <div class="preview-comment">
* Objects that are {@linkplain Class#isValue() value objects} do not have identity
* and can not be used as keys in a {@code WeakHashMap}. {@linkplain java.lang.ref.Reference References}
* such as {@linkplain WeakReference WeakReference} used by {@code WeakhashMap}
* to hold the key cannot refer to a value object.
* Methods such as {@linkplain #get get} or {@linkplain #containsKey containsKey}
* will always return {@code null} or {@code false} respectively.
* The methods such as {@linkplain #put put}, {@linkplain #putAll putAll},
* {@linkplain #compute(Object, BiFunction) compute}, and
* {@linkplain #computeIfAbsent(Object, Function) computeIfAbsent} or any method putting
* a value object, as a key, throw {@link IdentityException}.
* </div>
* </div>
*
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*
Expand Down Expand Up @@ -288,6 +304,8 @@ static Object unmaskNull(Object key) {
/**
* Checks for equality of non-null reference x and possibly-null y. By
* default uses Object.equals.
* The key may be a value object, but it will never be equal to the referent
* so does not need a separate Objects.hasIdentity check.
*/
private boolean matchesKey(Entry<K,V> e, Object key) {
// check if the given entry refers to the given key without
Expand Down Expand Up @@ -456,9 +474,11 @@ Entry<K,V> getEntry(Object key) {
* {@code null} if there was no mapping for {@code key}.
* (A {@code null} return can also indicate that the map
* previously associated {@code null} with {@code key}.)
* @throws IdentityException if {@code key} is a value object
*/
public V put(K key, V value) {
Object k = maskNull(key);
Objects.requireIdentity(k);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int i = indexFor(h, tab.length);
Expand Down Expand Up @@ -548,6 +568,7 @@ private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
*
* @param m mappings to be stored in this map.
* @throws NullPointerException if the specified map is null.
* @throws IdentityException if any of the {@code keys} is a value object
*/
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
Expand Down
2 changes: 1 addition & 1 deletion test/jdk/java/lang/Class/GenericStringTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, 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
Expand Down
4 changes: 2 additions & 2 deletions test/jdk/java/util/Collection/MOAT.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, 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
Expand All @@ -26,7 +26,7 @@
* @bug 6207984 6272521 6192552 6269713 6197726 6260652 5073546 4137464
* 4155650 4216399 4294891 6282555 6318622 6355327 6383475 6420753
* 6431845 4802633 6570566 6570575 6570631 6570924 6691185 6691215
* 4802647 7123424 8024709 8193128 8327858
* 4802647 7123424 8024709 8193128 8327858 8346307
* @summary Run many tests on many Collection and Map implementations
* @author Martin Buchholz
* @modules java.base/java.util:open
Expand Down
Loading