< prev index next >

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Print this page

        

@@ -420,10 +420,14 @@
      * A lookup can fail, because
      * the containing class is not accessible to the lookup class, or
      * because the desired class member is missing, or because the
      * desired class member is not accessible to the lookup class, or
      * because the lookup object is not trusted enough to access the member.
+     * In the case of a field setter function on a {@code final} field,
+     * finality enforcement is treated as a kind of access control,
+     * and the lookup will fail, except in special cases of
+     * {@link Lookup#unreflectSetter Lookup.unreflectSetter}.
      * In any of these cases, a {@code ReflectiveOperationException} will be
      * thrown from the attempted lookup.  The exact class will be one of
      * the following:
      * <ul>
      * <li>NoSuchMethodException &mdash; if a method is requested but does not exist

@@ -1436,10 +1440,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can store values into the field
          * @throws NoSuchFieldException if the field does not exist
          * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+         *                                or {@code final}
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          * @see #findVarHandle(Class, String, Class)
          */

@@ -1559,10 +1564,11 @@
          * @param name the field's name
          * @param type the field's type
          * @return a method handle which can store values into the field
          * @throws NoSuchFieldException if the field does not exist
          * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+         *                                or is {@code final}
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {

@@ -1838,14 +1844,14 @@
 
         /**
          * Produces a method handle giving read access to a reflected field.
          * The type of the method handle will have a return type of the field's
          * value type.
-         * If the field is static, the method handle will take no arguments.
+         * If the field is {@code static}, the method handle will take no arguments.
          * Otherwise, its single argument will be the instance containing
          * the field.
-         * If the field's {@code accessible} flag is not set,
+         * If the {@code Field} object's {@code accessible} flag is not set,
          * access checking is performed immediately on behalf of the lookup class.
          * <p>
          * If the field is static, and
          * if the returned method handle is invoked, the field's class will
          * be initialized, if it has not already been initialized.

@@ -1855,42 +1861,55 @@
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
             return unreflectField(f, false);
         }
-        private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
-            MemberName field = new MemberName(f, isSetter);
-            assert(isSetter
-                    ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
-                    : MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
-            @SuppressWarnings("deprecation")
-            Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;
-            return lookup.getDirectFieldNoSecurityManager(field.getReferenceKind(), f.getDeclaringClass(), field);
-        }
 
         /**
          * Produces a method handle giving write access to a reflected field.
          * The type of the method handle will have a void return type.
-         * If the field is static, the method handle will take a single
+         * If the field is {@code static}, the method handle will take a single
          * argument, of the field's value type, the value to be stored.
          * Otherwise, the two arguments will be the instance containing
          * the field, and the value to be stored.
-         * If the field's {@code accessible} flag is not set,
+         * If the {@code Field} object's {@code accessible} flag is not set,
          * access checking is performed immediately on behalf of the lookup class.
          * <p>
-         * If the field is static, and
+         * If the field is {@code final}, write access will not be
+         * allowed and access checking will fail, except under certain
+         * narrow circumstances documented for {@link Field#set Field.set}.
+         * A method handle is returned only if a corresponding call to
+         * the {@code Field} object's {@code set} method could return
+         * normally.  In particular, fields which are both {@code static}
+         * and {@code final} may never be set.
+         * <p>
+         * If the field is {@code static}, and
          * if the returned method handle is invoked, the field's class will
          * be initialized, if it has not already been initialized.
          * @param f the reflected field
          * @return a method handle which can store values into the reflected field
-         * @throws IllegalAccessException if access checking fails
+         * @throws IllegalAccessException if access checking fails,
+         *         or if the field is {@code final} and write access
+         *         is not enabled on the {@code Field} object
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
             return unreflectField(f, true);
         }
 
+        private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
+            MemberName field = new MemberName(f, isSetter);
+            if (isSetter && field.isStatic() && field.isFinal())
+                throw field.makeAccessException("static final field has no write access", this);
+            assert(isSetter
+                    ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
+                    : MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
+            @SuppressWarnings("deprecation")
+            Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;
+            return lookup.getDirectFieldNoSecurityManager(field.getReferenceKind(), f.getDeclaringClass(), field);
+        }
+
         /**
          * Produces a VarHandle giving access to a reflected field {@code f}
          * of type {@code T} declared in a class of type {@code R}.
          * The VarHandle's variable type is {@code T}.
          * If the field is non-static the VarHandle has one coordinate type,
< prev index next >