Skip to content

Tiny typos in Date Time articles ( https://dev.java/learn/date-time/ ) and Reflection articles ( https://dev.java/learn/reflection ) #191

Open

Description

@willy-b

Thanks for the excellent content!

Just sharing a few small typos I found in case the team wants to clean them up in the next version.


The following code, **efines** the departure time for a flight from San Francisco to Tokyo as a [ZonedDateTime](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/ZonedDateTime.html) in the America/Los Angeles time zone.

efines should be defines .

Image
Image

has a typo, "refence" instead of "reference."

Image

there are a few issues.

First, a small typo in:

Note that there are restrictions on the members you can make accessible, if there are in other modules. You can check the page [Reflective Access with Open Modules and Open Packages](https://dev.java/learn/modules/opening-for-reflection/) for more information.

That is, "if there are in other modules", "there" should be "they." (see screenshot below, shared with one more example).

Image

Then for: "This codes makes the field nameField accessible, despite being private."
It seems it should be "This code makes" instead of "This codes makes" (with an extra "s" on "codes") (see screenshot above).

Finally, in two code snippets, the "toString()" method for the User class is overridden, and both of them have a typo in the String returned.
That is, in two places:

    public String toString() {
        return "User[name='" + name + "]"; // missing single quote before square bracket on right side of name
    }

should be:

    public String toString() {
        return "User[name='" + name + "']"; // single quote added before square bracket on right side of name
    }

A side note, in the example, it is not mentioned that,
for

Field nameField = userClass.getDeclaredField("name");

NoSuchFieldException must be caught or declared to be thrown,
and

nameField.set(maria, "Mary");

IllegalAccessException must be caught or declared to be thrown, which might also be worth mentioning.

Image

In

You can still get access to such a method and invoke it, by first calling [method.setAccessible(true)](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/reflect/Method.html#setAccessible(boolean)) on it. 
This call may fail in case you are trying to invoke a method from a class in another module, 
due to the restrictions on the class members a module. You can check the page [Reflective Access with Open Modules and Open Packages](https://dev.java/learn/modules/opening-for-reflection/) for more information.

Not sure what wording exactly was intended to be written for "due to the restrictions on the class members a module," which is not grammatically correct. It may be missing a preposition before "a module" or might be a leftover fragment that should simply be removed. Perhaps the rest of the sentence prior is fine by itself, e.g. "This call may fail in case you are trying to invoke a method from a class in another module," because the package containing the class of interest may not be opened or exported by the other module, which is already explained by the link given in the next sentence.

Image

Also "But by doing so you loose the type safety of calling the method directly."
"loose" should be "lose" in this case.

Image

Thanks so much for the all the content!

Bit hesitant to bother you with the reports of the typos and other little issues, but if I were hosting this I would want these reported (and they are very easy to clean up), so letting you know.

Activity

  1. JosePaumard commented on Feb 8, 2026

    @JosePaumard
    Contributor

    Hello, and thank you for taking the time to report all this! I just pushed the fixes.
    is "This call may fail in case you are trying to invoke a method from a class in another module, due to the access restrictions on class members that a module may declare." correct, and is it clear enough?
    Again, thank you!

  2. willy-b commented on Feb 8, 2026

    @willy-b
    Author

    Thanks for the updates!

    All typos I had reported in the Date Time articles have been resolved.

    (As of https://web.archive.org/web/20260208223138/https://dev.java/learn/date-time/zoneid-zone-offset/ and https://web.archive.org/web/20260208223211/https://dev.java/learn/date-time/temporal/ .)

    For the Reflection article on Fields ( https://dev.java/learn/reflection , section https://dev.java/learn/reflection/fields/ ; archived at https://web.archive.org/web/20260208223622/https://dev.java/learn/reflection/fields/ ) , there are a couple of items remaining:


    Re: your question on how you should finish the sentence

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it.
    This call may fail in case you are trying to invoke a method from a class in another module,

    on https://dev.java/learn/reflection/methods/ (current version with your proposed edit archived at https://web.archive.org/web/20260208223938/https://dev.java/learn/reflection/methods/ ):

    is "This call may fail in case you are trying to invoke a method from a class in another module, due to the access restrictions on class members that a module may declare." correct, and is it clear enough?

    I think "due to the access restrictions of class members that a module may declare"
    is a little confusing to be honest because, as I understand it (see the spec quote below):

    1. the reflective access is restricted at the package, not class level
    2. from outside a module, reflective access to packages is restricted by default (so a module does not need to declare access restrictions to block reflective access); unless packages are exported or opened, the classes within are not accessible to reflection.

    Per JLS 25:
    https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf

    A normal module
    grants reflective access to types in only those packages which are explicitly
    exported or explicitly opened (or both). An open module grants reflective access
    to types in all its packages, as if all packages had been opened.
    
    For code outside a normal module, the reflective access granted to types in the
    module's exported (and not opened) packages is specifically to the public and
    protected types in those packages, and the public and protected members of
    those types. The reflective access granted to types in the module's opened packages
    (whether exported or not) is to all types in those packages, and all members of those
    types. No reflective access is granted to types, or their members, in packages which
    are not exported or opened
    

    Or e.g. also per https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/reflect/AccessibleObject.html

    Whether the checks for Java language access control can be suppressed (and thus, whether access can be enabled) depends on whether the reflected object corresponds to a member in an exported or open package (see setAccessible(boolean)).

    Thanks very much!

  3. JosePaumard commented on Feb 9, 2026

    @JosePaumard
    Contributor

    Thank you for your review and reports! I just pushed some fixes.
    As for the exception handling part: I prefer to keep things simple in this section, by not deep-diving into to many details. This part is about Reflection, and there is a section dedicated to Exceptions. BTW I changed the wording to "can throw an Exception".
    As for the modules: thanks for your comment. I rewrote this part. And, same as for the Exception, since there is a section specifically on this topic, I don't want to go too deep neither.

  4. willy-b commented on Feb 10, 2026

    @willy-b
    Author

    Great! The discussion of the Exceptions is clear and agreed makes sense to leave out any additional detail on that.


    Re: https://dev.java/learn/reflection/methods/
    and replacing what you had written in the 2nd version ( https://web.archive.org/web/20260206041438/https://dev.java/learn/reflection/methods/ )

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it. 
    This call may fail in case you are trying to invoke a method from a class in another module, 
    due to the access restrictions on class members that a module may declare.
    

    (which I said in #191 (comment) was confusing since package level control is the finest level of export/opening control available for modules, not classes or class members, and defined modules hide packages by default from reflection from other modules)

    with the new ( https://web.archive.org/web/20260210144803/https://dev.java/learn/reflection/methods/ ):

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it. 
    This call may fail in case the element you want to make accessible belongs to a named module, 
    and this named module does not declare itself 
    or the package that contains the element you want to access as being opened to reflection.
    

    Agreed on the change to the last half of the second sentence:
    "module does not declare itself or the package that contains the element you want to access as being opened to reflection" is great and addresses my concern about other explicitly defined modules being closed for reflection by default, and that opening contents of a module for reflection is at its finest level controlled at the package, not class or class member level, though "element" might be better replaced with "method" as this article is specific to methods.

    Re: replacing

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it. 
    This call may fail in case you are trying to invoke a method from a class in another module,
    

    with

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it. 
    This call may fail in case the element you want to make accessible belongs to a named module,
    

    maybe instead of "the element you want to make accessible belongs to a named module", "you are trying to invoke a method from a class in another named module" would be clearest,
    as this is from https://dev.java/learn/reflection/methods/ and per the previous sentence is referring to method.setAccessible(true) and methods from classes in the same named module are still open to reflection.

    Thanks for also specifying "named module", as I agree it is worth mentioning explicitly that if one does not define a module at all, despite the fact that an unnamed module is created automatically, the classes without a declared module are treated the same for reflection by other modules that can import them as if they were in the same module rather than another module (open to reflection; as used to be the case in Java generally for all packages before reflection was restricted by modules, which first landed in a long-term supported java release in 2018-09 with Java 11, first out in 2017-09 in Java 9).

    E.g. from the JLS 25 spec:
    https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf

    7.7 Module Declarations
    A module declaration specifies a new named module [(if a module is declared, it is a named module)]
    [...]
    7.7.5 Unnamed Modules
    An observable ordinary compilation unit that the host system does not associate
    with a named module (§7.3) is associated with an unnamed module.
    Unnamed modules are provided by the Java SE Platform in recognition of the fact
    that programs developed prior to Java SE 9 [released in 2017-09] could not declare named modules.
    [...]
     An unnamed module exports every package whose ordinary compilation units
    are associated with that unnamed module.
    An unnamed module opens every package whose ordinary compilation units are
    associated with that unnamed module.
    

    The last thing would then be to consider to make the detail enhancement you made above also in https://dev.java/learn/reflection/fields/ (archived: https://web.archive.org/web/20260210044513/https://dev.java/learn/reflection/fields/ ), which currently reads:
    "Note that there are restrictions on the members you can make accessible, if they are in other modules."
    Where you could also expand what your team had written there, "if they are in other modules", to "if they are in other named modules" as we discussed here.


    Otherwise, for https://dev.java/learn/reflection/fields/ (archived: https://web.archive.org/web/20260210044513/https://dev.java/learn/reflection/fields/ ), issues reported were all resolved:

    Thanks so much!

  5. JosePaumard commented on Feb 10, 2026

    @JosePaumard
    Contributor

    What I see is that this subject of getting access to non-visible fields is a little clumsy. So I did some reorganization.
    I added a section on accessing private members on the Field page, and a similar section on the Method and Constructor pages, that point to the Field page. I also changed the explanation a little about the reflective access.

  6. willy-b commented on Feb 11, 2026

    @willy-b
    Author

    Thanks so much for the changes!

    For the latest version, reviewing all sections so far discussed,
    I see the following issues:


    In the latest https://dev.java/learn/reflection/methods/ (archived: https://web.archive.org/web/20260210180629/https://dev.java/learn/reflection/methods/ ):

    For the sentence:
    "The type parameter of a method is erased at compile time, meaning that you cannot use it with the Reflection API."
    I think "type parameter" should be plural, so this sentence should instead be written:
    "The type parameters of a method are erased at compile time, meaning that you cannot use them with the Reflection API."

    An example of a method with multiple type parameters would be an implementation of Map#put(K,V):
    https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Map.html#put(K,V) .

    E.g. one replacing the current single type example with a two type example as follows:

    import java.util.Map;
    import java.util.HashMap;
    // [...]
    Map<String, Integer> wordCount = new HashMap<String, Integer>();
    Class<?> wordCountClass = wordCount.getClass();
    
    // NoSuchMethodException due to type erasure
    // Method putMethod = wordCountClass.getMethod("put", String.class, Integer.class);
    
    // this invocation, using erased types will work
    Method putMethod = wordCountClass.getMethod("put", Object.class, Object.class);
    

    For the updated discussion of limitations on setAccessible in the methods reflection article:

    You can still get access to such a method and invoke it, by first calling method.setAccessible(true) on it.
    As for fields, there are restrictions on the methods you can make accessible.
    You can read more on this subject in the section Making a Field, a Method or a Constructor Accessible.
    

    Thanks, this is clear. The detail we were discussing in #191 (comment) has been moved to the new article section "Making a Field, a Method or a Constructor Accessible" of the https://dev.java/learn/reflection/fields/ article, discussed later in this comment (see below), and I think consolidating the explanation and expanding it there was a smart move.


    For https://dev.java/learn/reflection/fields/ (archived: https://web.archive.org/web/20260210181106/https://dev.java/learn/reflection/fields/ )

    • "A Field object let you get more information on the corresponding field" ("let" -> "lets")
    • "Locating Field" header missing "s" ("Locating Fields")
    • Not technically an error but seems unintentional:
      In the first example of the section "Locating Field" the line boolean isPublic = field.accessFlags().contains(AccessFlag.PUBLIC); defines a variable which is not used (does not actually get printed in the print statement on the subsequent line)
    • In the sentence, "Indeed, there is no public fields in ArrayList, nor in any of its super classes," "there is" should be "there are."
    • In the sentence, "The method Field.getModifiers() returns an int in which each bit represent a modifier", "represent" should be "represents."
    • Re: new phrasing around setAccessible visibility limitations and modules (moving everything to a separate section - used to be a separate article but with some content still here)
    Note that there are restrictions on the fields you can make accessible.
    You can read more on this subject in the section Making a Field, a Method or a Constructor Accessible.
    

    This is safe as far as what is mentioned here in this section.

    This call [(setAccessible)] will fail if the element you want to make accessible belongs to another, named module,
    and this named module does not declare itself or the package that contains the element you want to access
    as being opened to reflection.
    By default a named module is closed for reflective access,
    meaning that other named modules, and the unnamed module cannot access its non-public classes,
    and the non-public members of its public classes.
    
    If you are working on an application that does not use the Java Module System,
    then you can safely use the Reflection API to access all the elements of your application,
    as everything is part of the unnamed module [me: add link here to https://dev.java/learn/modules/unnamed-module/ ?] .
    

    Thank you, this is so much clearer than the earlier versions,
    e.g. "Note that there are restrictions on the members you can make accessible, if they are in other modules." from https://web.archive.org/web/20260210044513/https://dev.java/learn/reflection/fields/ or "This call may fail in case you are trying to invoke a method from a class in another module, due to the restrictions on the class members a module." from https://web.archive.org/web/20260206041438/https://dev.java/learn/reflection/methods/ .
    My last remaining thoughts on this is are:

    • Why not link here also to your team's article on "the unnamed module" at https://dev.java/learn/modules/unnamed-module/ (archived: https://web.archive.org/web/20260211015421/https://dev.java/learn/modules/unnamed-module/ )?

    • I can't comment on whether not using the Java Module System is a bad practice or not (I am not familiar with the latest recommendations on that yet) and whether it is safe for everything to be in the unnamed automatically generated module which is entirely open for reflection in that case, but it is how the entire Java language worked prior to Java 9 in 2017; modules and restrictions on reflective access only first even became long-term supported in 2018 with Java 11, and are still not required to be used. I do agree though that in a project not using the Java Module System the setAccessible call is safe in that it will not fail due to reflective access restrictions, and I agree that this paragraph is consistent with what your team wrote in https://dev.java/learn/modules/unnamed-module/ .

    • It may be appropriate to say "automatically generated unnamed modules" instead of "the unnamed module," and to note that all declared modules are named modules, see https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf section 7.7 (see also 7.7.5). Note that in the original planning for the module system there was described a single "unnamed module" referred to as "the unnamed module" e.g. in https://openjdk.org/projects/jigsaw/spec/sotms/#the-unnamed-module , but in the JLS 25 and more recent documents, unnamed modules are described in the plural and e.g. https://docs.oracle.com/en/java/javase/25/docs/api//java.base/java/lang/Module.html says "There is an unnamed module for each ClassLoader, obtained by invoking its getUnnamedModule method. All types that are not in a named module [(all declared modules are named)] are members of their defining class loader's unnamed module."

    • Under the heading "Dealing with Primitive Types", in the sentence, "Is your field has a primitive type or a wrapper type," "Is" should be "If"


    For the latest "Opening for Reflection" article, https://dev.java/learn/modules/opening-for-reflection/ (archived: https://web.archive.org/web/20260210193234/https://dev.java/learn/modules/opening-for-reflection/ ):

    • In the sentence, "communicates the intend to use the package with a reflection-based framework", "intend" should be "intent."

    • In the sentence, "Consequently, it doesn't make sense to manually open further packages, which is why opens directives in an open module and lead to compile errors." should probably end with "which is why additional "opens" directives in an open module lead to compilation errors." ("opens" rather than "open" intentional in this case.)


    In the latest https://dev.java/learn/reflection/constructors/ (archived: https://web.archive.org/web/20260210222645/https://dev.java/learn/reflection/constructors/),

    • In the sentence "The second pattern that you should be using now, consists in getting a reference on one of the constructor of the class, and invoke it.",
      "constructor" should be "constructors", and "invoke" should be "invoking."
    • In the sentence "the call to newInstance() throws several exception:",
      "exception:" should be "exceptions:".

    On the latest https://dev.java/learn/reflection/names/ (archived: https://web.archive.org/web/20260211003456/https://dev.java/learn/reflection/names/ ):

    • In the sentence, "The Reflection API defines several convention for class names, that are all covered in this section.", "convention" should be "conventions."
    • "The canonical name of a class is defined in the specification of a language." Should it be "the language?"
    • "The behavior is different for arrays, as you are going to see it in the next section", should be "The behavior is different for arrays, as you are going to see in the next section."

    On the latest https://dev.java/learn/reflection/modifiers/ (archived at https://web.archive.org/web/20260211002358/https://dev.java/learn/reflection/modifiers/ ):

    • The sentence "These modifiers can modify the visibility of the element there are applied to, or the fact that they can be overridden, or made non-modifiable.", has grammatical issues (e.g. "there are" instead of "they are")
    • In the sentence, "It also contains methods which may be used to decode the set of modifiers returned by the method Class.getModifiers()`." the link is broken by a stray back-tick.
    • In the sentence, "You can get more information on this point in the documentatoin of the AccessFlag enumeration.", "documentatoin" should be "documentation."

    Thanks again!
    I will continue reviewing later pages in the tutorials for typos and other issues and once you let me know the ones I have reported so far are fixed, I will post the next batch that I find (if any). Looking forward to helping your team get these nits and typos completely cleared out.

  7. JosePaumard commented on Feb 11, 2026

    @JosePaumard
    Contributor

    Thank you for your review and reports! I just pushed the fixes.
    As for the unnamed module: there is a section dedicated to this topic, so I prefer to keep it simple in the Reflection part.

  8. NamLe029 commented on Feb 11, 2026

    @NamLe029

    There is also a typo in https://dev.java/learn/reflection/classes/#get-class, at the end of the subsection Getting a Runtime Class:

    You will a way to get the generic type of elements see later in this chapter.
    
  9. JosePaumard commented on Feb 11, 2026

    @JosePaumard
    Contributor

    Thank you for your report! I just pushed a fix.

  10. willy-b commented on Feb 23, 2026

    @willy-b
    Author

    Thanks! The specific issues I previously reported have been addressed (except where you disagreed explicitly with justification).

    Here are some additional typos and small errors I have found in these articles as I continue to review them:


    For the latest https://dev.java/learn/reflection/annotations/ (archived at https://web.archive.org/web/20260222211141/https://dev.java/learn/reflection/annotations/ )

    • In the "ReThrowAsRuntimeException" example, the value computed for the annotationPresent variable is NOT actually used and a RuntimeException is thrown regardless
    AnnotatedType[] annotatedExceptionTypes = constructor.getAnnotatedExceptionTypes();
        for (AnnotatedType annotatedExceptionType : annotatedExceptionTypes) {
            boolean annotationPresent =
                    annotatedExceptionType.isAnnotationPresent(ReThrowAsRuntimeException.class);
            throw new RuntimeException(e);
        }
    
    Image
    • In "allows you to check is a given annotation is present on this element", the first "is" should probably be "if".

    • "getAnnotation(Class) and getDeclaredAnnotation(Class): returns an annotation for the specific type passed as an argument." , I think "returns" should be "return" to match the plural subject

    • "getAnnotationsByType(Class has been added in Java SE 8" is missing a closing ")"

    • In "First, you can check if the Person class has any annotation, and discover them.", "annotation" should be plural.

    • "Note that this annotation can be used where the types are used, not the types themselves."
      might be clearer as "Note that this annotation can be used where the types are used, not applied to the types themselves."

    • In "You can the use this annotation in this way", "the" should be "then"

    • In the "Getting Inherited Annotations" and subsequent sections, there seem to be a few accidental replacements of "User" with "Employee"
      e.g.

    
    You can examine the available annotations on the User class with the following code.
    
    Class<?> c = Employee.class;
    
    [... example continues, using Employee, not User ...]
    
    
    Image

    and in the first example of the "Getting Annotations on Types" section:

    Image

    (which also has typos, see above, in "You can the use this annotation" ("the" should be "then"), and "gives you access to this annotation in ths way" ("ths" should be "this"))


    In the latest version of the "Creating an Interceptor with Annotations" article, https://dev.java/learn/reflection/interceptor/ (archived at https://web.archive.org/web/20260222211217/https://dev.java/learn/reflection/interceptor/ ), there are a few possible issues:

    • "Now that you have all the elements, you can finally run this code"
      is written one extra time before ,
      "Now that you have all the elements, you can run the following code,"
      in a way that makes it look like the flow was interrupted by the "Let us add another class, to show that the interception mechanism is working correctly." section and then resumed.
      It may be clearer if the first occurrence is dropped.
    Image
    • SomeInterceptedService is declared "public class",

    but after writing
    "Let us add another class, to show that the interception mechanism is working correctly. This SomeNonInterceptedService class is the same as the SomeInterceptedService class.
    The only difference is that its service method does not declare the interceptor",
    another unnecessary difference is introduced:
    SomeInterceptedService is declared static (actually "public static class" which is an error, see above, but it should be corrected to "public class" not non-public static), which is inconsistent and maybe distracting and looks unintentional though it is harmless.

    Image
    • re: ServiceFactory.invoke, do we need to be able to invoke non-public methods on a service class using the ServiceFactory? If not, we can replace getDeclaredMethod with getMethod?

    • It should probably be mentioned that the approach used in ServiceFactory.invoke is not safe for many method parameter types, e.g. interface or superclass types of those used at runtime (ignoring generics which would also be problematic).

    It may be necessary instead to iterate through getDeclaredMethods() (or if only public methods are needed, getMethods()) to find a possible match,
    taking into account argument superclass and superinterface relationships, using e.g. Class::isAssignableFrom (i.e. requiring methodParameterTypes[i].isAssignableFrom(parameterClasses[i]) for all i) (see https://web.archive.org/web/20251204003020/https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Class.html#isAssignableFrom(java.lang.Class) ).

    In more detail,

    for ServiceFactory.invoke:

    public static <T, R> R invoke(
                Class<? extends T> serviceClass, String methodName, Object... arguments) {
    // [...]
                // Locating the service method
                Class<?>[] parameterClasses =
                        Arrays.stream(arguments).map(Object::getClass).toArray(Class<?>[]::new);
                Method method = serviceClass.getDeclaredMethod(methodName, parameterClasses);         
    

    Using getDeclaredMethod to look for the applicable method using the runtime type of the argument makes this fail for any case where the runtime type does not match exactly, e.g.
    when the method parameter type is an interface or superclass (also problematic for primitives).

    For a specific example, one can try the following almost identical variant of the intercepted service method, changing the parameter type to the CharSequence interface (String is an implementation of CharSequence and can be passed as such a parameter):

    public class SomeInterceptedService {
        @Intercept(MessageInterceptor.class)
        public String message(CharSequence input) {
            return input.toString().toUpperCase();
        }
    }
    public class SomeNonInterceptedService {
        public String message(CharSequence input) {
            return input.toString().toUpperCase();
        }
    }
    

    Then the ServiceFactory can no longer invoke that method with a String argument, throwing a
    NoSuchMethodException, as getDeclaredMethod cannot use the runtime type of the argument, a String, an implementation of CharSequence, to look up a method with a CharSequence parameter:

    $ java Main
    Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchMethodException: SomeInterceptedService.message(java.lang.String)
    	at ServiceFactory.invoke(ServiceFactory.java:36)
    	at Main.main(Main.java:6)
    Caused by: java.lang.NoSuchMethodException: SomeInterceptedService.message(java.lang.String)
    	at java.base/java.lang.Class.getDeclaredMethod(Class.java:2848)
    	at ServiceFactory.invoke(ServiceFactory.java:15)
    	... 1 more
    

    If one replaces
    Method method = serviceClass.getDeclaredMethod(methodName, parameterClasses);
    with
    Method method = findMatchingMethod(serviceClass, methodName, parameterClasses);
    for an appropriately defined static method findMatchingMethod
    which iterates through serviceClass.getDeclaredMethods() for one with a matching methodName, the same number of parameters, and which satisfies methodParameterTypes[i].isAssignableFrom(parameterClasses[i] for all indices i when methodParameterTypes = method.getParameterTypes(), it resolves at least this issue and this example (one will also have to disambiguate the cases with multiple matching signatures by following the Java rule of most specific parameter types winning) (I have written code to handle the single matching method case and can confirm the NoSuchMethodException above is resolved for e.g. a parameter type of CharSequence with runtime type of String).


    In the latest version of the Dependency Injection using Reflection article, https://dev.java/learn/reflection/dependency-injection/ (archived at https://web.archive.org/web/20260222211245/https://dev.java/learn/reflection/dependency-injection/ ):

    (Very cool tutorial!)

    I think the @Target for the @Inject annotation might be mistaken, it is given as ElementType.TYPE,
    when it appears to be a case calling for a ElementType.FIELD annotation:

    @Target(ElementType.TYPE) // should be FIELD?
    @Retention(RetentionPolicy.RUNTIME)
    @interface Inject {
    }
    
    @Singleton // annotating a TYPE
    public class MyApplication {
        @Inject // annotating a FIELD
        private DBService dbService;
        
        public boolean isDBServiceSet() {
            return dbService != null;
        }
    }
    
    Image

    Trying to use an ElementType.TYPE on a field declaration will give an annotation interface not applicable to this kind of declaration error, I think.

    Aside from that, there are also similar issues here as reported above for the Interceptor article
    with using reflection to find a method (in this case a constructor) that is an exact match by argument runtime type rather than a compatible match.
    E.g.
    BeanFactory.getInstanceOf uses

    
    Class<?>[] argumentsClasses =
                    Arrays.stream(arguments).map(Object::getClass).toArray(Class<?>[]::new);
                
    // Locating the corresponding constructor
    Constructor<T> beanConstructor = beanClass.getConstructor(argumentsClasses);
    

    which won't find a constructor that has any parameters of interface types or superclass types to the types passed at runtime .
    For example, this will not be able find a constructor with CharSequence parameters if a String is passed at runtime, e.g., if Message uses CharSequence:

    public record Message(CharSequence message) {
        public Message {
            Objects.requireNonNull(message);
        }
    }
    

    The current BeanFactory.getInstanceOf would not be able to construct a Message with a CharSequence component type with a String despite CharSequence.class.isAssignableFrom(String.class) == true .

    That is not to say this approach cannot be used, but a simple call to getConstructor with the argument classes will not yield matching constructors if interface or superclass types are used, not to mention generics (e.g. suppose the constructor takes a Collection, or Map, or List - it will fail).

    It may be necessary to iterate through getDeclaredConstructors() instead to find a possible match,
    taking into account argument superclass and superinterface relationships, using Class::isAssignableFrom (i.e. requiring methodParameterTypes[i].isAssignableFrom(parameterClasses[i]) for all i) (see https://web.archive.org/web/20251204003020/https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Class.html#isAssignableFrom(java.lang.Class) ).


    In the latest version of the Enums Reflection article, https://dev.java/learn/reflection/enums/ (archived at https://web.archive.org/web/20260222211043/https://dev.java/learn/reflection/enums/ ):

    • In "Note that this enum as a defined constructor, a field, and a method, which is an accessor to this field", "this enum as a" should be "this enum has a".

    • In "It also has a factory method, to get one of the constant from a label.", the word "constant" should be plural, "constants".


    In the latest version of the Records Reflection article, https://dev.java/learn/reflection/records/ (archived at https://web.archive.org/web/20260222211125/https://dev.java/learn/reflection/records/ ):

    • Re: "Records are a particular type of class, that strongly enforce non-modifiability. It also introduces the notion of record component." Maybe "it also introduces" should be "they also introduce" due to plural subject ? (see previous sentence, "Records are")

    • Re: "When were introduced in Java SE 16, several new methods and classes were added to the Reflection API." Should this start "When Records were introduced"?


    In the latest version of the Date Time APIs TOC page, https://dev.java/learn/date-time/ (archived: https://web.archive.org/web/20260210102410/https://dev.java/learn/date-time/ ), a few typos:

    • "The Instant class represent an instantaneous moment on the timeline.": "represent" -> "represents"

    • Re: "The Period and Duration classes, as well as the ChronoUnit.between method() are used to calculate an amount of time."
      Should the "()" be on "between" instead of "method" or not present at all?

    • Re: "tips on how to convert older java.util.Date and java.util.Calendar code to the Date-Time API." This section description sentence does not begin with a capital letter, unlike the others on this page.


    In the latest version of the Methods Reflection article, https://dev.java/learn/reflection/methods/ , (archived at https://web.archive.org/web/20260222204250/https://dev.java/learn/reflection/methods/ ):

    • In the sentence "The method Method.getModifiers() returns an int in which each bit represent a single modifier", the word "represent" should be "represents". (If this sounds like a repeat, it is because I reported the same typo in the text for Field.getModifiers in the Fields reflection article earlier.)

    • Re: "On the other hand, if the method you are invoking takes two parameters, then you invocation of the invoke() method needs three arguments". It seems "you invocation" should be "your invocation".

    • Re: "The String.length() does not take any parameter, which make the invocation of this method easier." Can drop the leading article "The" or write "The method String.length()" but as is, saying "The String.length()" seems wrong. Also, "does not take any parameter" might be made consistent with other descriptions which assume the possibility of multiple parameters by writing "does not take any parameters". "which make" should also probably be "which makes."

    • Re: "The Reflection API catches these exceptions for you, and wrap them in a InvocationTargetException that you can catch and analyze."
      Here "wrap" should be "wraps."

    • Re: "Note that we are using the Files.newBufferedWriter() method, that declares a variable argument." It seems "that declares" should be "which declares".


    On the latest Reflection articles Table of Contents , https://dev.java/learn/reflection/ (archived at https://web.archive.org/web/20260222211626/https://dev.java/learn/reflection/ ), there are a couple of small issues.

    The "Records" section description is the just the first 13 words of the "Reading Modifiers" section's description, suggesting some kind of copy-paste error.

    Compare:

    Working with Records
    
    How to discover the modifiers of a class or of any class member
    
    

    and

        Reading Modifiers
    
        How to discover the modifiers of a class or of any class member: fields, methods, and constructors.
    
    Image

    Then, for the "Retrieving Classes" section, in the description sentence "How to retrieve a class instance, from its name, or from and object, including arrays an primitive types":

    • In "from and object", it should be "from an object" ("and" should be "an").
    • In "including arrays an primitive types", "an" should be "and."

    Thanks again for the excellent content and for resolving issues as they are reported!

  11. JosePaumard commented on Feb 23, 2026

    @JosePaumard
    Contributor

    Thank you for all this! I just pusehd a fix.
    As for the interceptor example. I agree that this example is fragile due to the difference between the types at compile time and the types at runtime, but I think this example is complex enough as it is. I just added a mention on this point, but showing how to fix it would be a whole new chapter, and I think it would go way outside the scope of this presentation on the Reflection API. Matching trees and graphs is not really the subject of this chapter ;)

  12. willy-b commented on Feb 28, 2026

    @willy-b
    Author

    Great! Most items reported (typos, class/variable name mismatches, etc) have been fixed. Thanks so much!

    However, it does seem at least a couple of the issues reported in #191 (comment) are still actually NOT fixed yet, see below.


    The following item reported earlier above remains in the latest https://dev.java/learn/reflection/annotations/ (archived at: https://web.archive.org/web/20260227183835/https://dev.java/learn/reflection/annotations/ , originally archived at https://web.archive.org/web/20260222211141/https://dev.java/learn/reflection/annotations/ ):

    • In the "ReThrowAsRuntimeException" example, the value computed for the annotationPresent variable is NOT actually used and a RuntimeException is thrown regardless
    AnnotatedType[] annotatedExceptionTypes = constructor.getAnnotatedExceptionTypes();
        for (AnnotatedType annotatedExceptionType : annotatedExceptionTypes) {
            boolean annotationPresent =
                    annotatedExceptionType.isAnnotationPresent(ReThrowAsRuntimeException.class);
            throw new RuntimeException(e);
        }
    
    Image

    This has not changed in the most recent version, indeed it rethrows a RuntimeException, but it does so regardless of whether the new "ReThrowAsRuntimeException" annotation specifically is on the method (it could be another annotation for example, or even no annotations on the declared exception at all, as the boolean annotationPresent is not checked).


    For the latest version of the Interceptor article, https://dev.java/learn/reflection/interceptor/ ( archived at https://web.archive.org/web/20260227184909/https://dev.java/learn/reflection/interceptor/ ),

    I had raised a concern about reflective invoker (ServiceFactory::invoke) built in this tutorial not covering many cases, e.g. any method with interface parameter types (for which the runtime type of the parameter is not strictly equal to the parameter type of the method).
    E.g. a method taking an argument of type CharSequence which can be called with a String ( CharSequence assignable from String) but the reflective invoker using the overly specific getDeclaredMethod with the runtime type of the argument would fail in the case because it would not retrieve a method with argument type CharSequence by searching for one with a String type argument despite CharSequence.class.isAssignableFrom(String.class) == true (see Class::isAssignableFrom).

    Thanks for acknowledging this issue. I have a small question about the following text which has been added at the bottom to address that concern :

    Note that this example is working well because the types are defined in a nice way. 
    The invoke() method is looking for a method named message() that takes a String as a parameter. 
    The type String is read from the argument "message" you passed to the invoke() method. 
    If the message() method was defined on the CharSequence type instead of the String type (the type String extends the type CharSequence), then the getDeclaredMethod() call would not have returned this message() method.
    

    Re: what you have added above, "the type String extends the type CharSequence", this might be a bit confusing as String does not extend CharSequence (it is not a superclass), but rather it implements CharSequence as an interface.
    It might be clearer to stick to the narrow requirement, which is that CharSequence.class.isAssignableFrom(String.class) == true , rather than saying "String extends ... CharSequence".
    Thus, "(the type String extends the type CharSequence), then the getDeclaredMethod() call would not have returned this message() method." might be clearer as "If the parameter of the message() method was of type CharSequence instead of String, then despite CharSequence.class.isAssignableFrom(String.class) == true, the getDeclaredMethod() call used would not have returned this message() method." You might consider to mention that though it is out of scope for this tutorial, it is possible to loop through declared methods and check matching signatures using isAssignableFrom to resolve this gap.


    re: the latest version of https://dev.java/learn/reflection/dependency-injection/ , https://web.archive.org/web/20260227192258/https://dev.java/learn/reflection/dependency-injection/

    The reflective constructor invocation by getInstanceOf/instantiateBeanClass in this article was also reported above as not safe (will not cover any constructors which have interface parameter types for example), for the same reasons as discussed in the interceptor example above. No note has been added yet and it probably should be commented upon similar to what you did in the Interceptor article.


    Thanks so much!

  13. JosePaumard commented on Mar 2, 2026

    @JosePaumard
    Contributor

    Thank you again WIlly, for all these report! I just pushed a fix.
    As for the examples in the Reflection API part. Indeed the type String extends the type CharSequence. If may sound confusing, but this is the right way of saying it. Narrowing with isAssignableFrom() could solve this very simple problem, but if you add more parameters to the method you are looking for, then you will need much more complex code. Odds are that you will end up getting all the methods, and fitering this list on the name, and then on mutliple isAssignableFrom() on each parameter. And since methods can have overloads, you may have several results in the end, and need to choose which one to pick, probably by choosing the most precise one, or the closest to the real type you have, and then picking one randomly because you can still have ties.
    So I prefer to keep things simple.

  14. 26 remaining items

  15. JosePaumard commented on May 25, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  16. willy-b commented on May 30, 2026

    @willy-b
    Author

    Thanks, I can confirm all those items reported were fixed.

    I observe the following remaining items:


    In the latest version of https://dev.java/learn/reflection/classes/ (archived as is at https://web.archive.org/web/20260530043720/https://dev.java/learn/reflection/classes/ ),

    • For the example

      Class<?> c = System.console().getClass();
      IO.println("class: " + c);
      

      the expected output provided is

      class: c: class java.io.ProxyingConsole
      

      when it should be:

      class: class java.io.ProxyingConsole
      

      without the "c:".


    In the latest version of https://dev.java/learn/reflection/constructors/ (archived as is https://web.archive.org/web/20260530233259/https://dev.java/learn/reflection/constructors/ )

    • In the sentences

      Class.newInstance() throws any exception thrown by the constructor, wrapped in an InvocationTargetException.
      This method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler.

      ", wrapped in an InvocationTargetException. " should be removed,
      as Class.newInstance() does NOT throw InvocationTargetException, but rather the underlying exceptions, which is the problem. Constructor.newInstance() on the other hand does throw Exceptions wrapped in InvocationTargetException, which you can then declare for compile-time exception checking.
      See the deprecation notice on Class.newInstance() (quoted from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/Class.html#newInstance() ):

      This method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.

    • In the code snippet with the line:

      IO.println("constructor with " + constructor.getParameterCount() + " parameter");
      

      " parameter" should be " parameter(s)".

      That way the current output:

      constructor with 1 parameter
      java.util.Collection
      constructor with 0 parameter
      constructor with 1 parameter
      int

      Would be updated to:

      constructor with 1 parameter(s)
      java.util.Collection
      constructor with 0 parameter(s)
      constructor with 1 parameter(s)
      int

    • In the next code snippet also:

          IO.println("constructor with " + constructor.getParameterCount() + " parameter");
      

      should have " parameter" updated to " parameter(s)" for the same reason.


    Thanks very much!

  17. JosePaumard commented on Jun 1, 2026

    @JosePaumard
    Contributor

    Thank you again for your reports! A fix is on its way.

  18. willy-b commented on Jun 4, 2026

    @willy-b
    Author

    Excellent, thanks! Just the following tiny non-blocker issues to fix or ACK "won't fix" below and then I will close this.


    In the latest https://dev.java/learn/reflection/intro/ (archived as is at https://web.archive.org/web/20260603221846/https://dev.java/learn/reflection/intro/ )

    • Not a blocker for closing, but I will note in passing that re: the sentences

      Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet.

      that https://openjdk.org/jeps/504 removing the Applet API (planned to be removed for 10 years) has landed in Java 26, and the Security Manager itself has been deprecated since Java 17 and is permanently disabled since https://openjdk.org/jeps/486 which landed in Java 24.

    Thus also on https://dev.java/learn/reflection/classes/ (archived as is at https://web.archive.org/web/20260603222215/https://dev.java/learn/reflection/classes/ ),

    Aside from java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect that you need to use to get reflective access to any element of a class have public constructors.

    can be simplified to

    [proposed] None of the classes in java.lang.reflect that you need to use to get reflective access to any element of a class have public constructors.

    (as ReflectPermission is deprecated.)

    • also

      class: class: class [[[I
      

      should be class: class [[[I.


    In the latest version of https://dev.java/learn/reflection/interceptor/ (archived as is at: https://web.archive.org/web/20260604021307/https://dev.java/learn/reflection/interceptor/ )

    • Non-blocker nit:
      In the sentences

      The concept of intercepting a method call is very simple.
      it relies on two elements: a service that has a service method, and an interceptor, that also carries a method.

      "it relies" should be capitalized to "It relies."


    In the latest version of https://dev.java/learn/reflection/dependency-injection/ (archived as is at https://web.archive.org/web/20260604031422/https://dev.java/learn/reflection/dependency-injection/ )

    • Non blocker nit:
      In the sentence

      The first one is the concept of singleton, and the second one is dependency injection itself.

      "concept of singleton" should be "concept of a singleton".


    Thanks very much!

  19. JosePaumard commented on Jun 4, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  20. willy-b commented on Jun 4, 2026

    @willy-b
    Author

    The Reflection tutorial series part of this ticket ( https://dev.java/learn/reflection ) has everything fixed which had been reported, and I think that series is now in a good state overall, thanks so much!

    However, I realize during the pre-close review that the separate Date-Time tutorial series ( https://dev.java/learn/date-time/ ) which has not been discussed much in this ticket due to the number of items found in the Reflection tutorial still has a couple of issues remaining which should probably be fixed or ACK'd "won't fix" as part of this ticket. Thus I begin listing remaining Date-Time tutorial issues below in case your team wants to fix them.


    In the latest version of https://dev.java/learn/date-time/zoneid-zone-offset/ (archived as is at https://web.archive.org/web/20260604223645/https://dev.java/learn/date-time/zoneid-zone-offset/ ),

    • The first code snippet on the page will not compile due to multiple errors in the code, and has out of date TZDB entries leading to differences in the output vs recent versions of Java

      Image

      First, the lines

      // Create a List using the set of zones and sort it.
      List<String> zoneList = new ArrayList<>(allZones).sort();
      

      will not compile, as List.sort must take a Comparator argument ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/List.html#sort(java.util.Comparator) )
      AND sort returns void.
      Thus one will get a message like (fixing this is not enough as sort also returns void):

       Error:
      |  method sort in class java.util.ArrayList<E> cannot be applied to given types;
      |    required: java.util.Comparator<? super java.lang.String>
      |    found:    no arguments
      |    reason: actual and formal argument lists differ in length
      |  List<String> zoneList = new ArrayList<>(allZones).sort();
      |                          ^----------------------------^
      

      The line above should be replaced with:

         List<String> zoneList = new ArrayList<>(allZones); 
       zoneList.sort(Comparator.naturalOrder());
      

      Then, two variables are given the same identifier "zone" and so the example would not compile also for this independent, second reason.
      Thus,

      for (String zone : zoneList) {
        ZoneId zone = ZoneId.of(zone); // [compilation error here, there is already a variable named "zone"]
        ZonedDateTime zdt = dt.atZone(zone); 
      

      should be updated to:

       for (String zone : zoneList) {
        ZoneId zoneId = ZoneId.of(zone);
        ZonedDateTime zdt = dt.atZone(zoneId);
      

      With those changes together, the example will compile and run and produce something close to what is given as the expected output.
      However, I then still observe that there are differences in the timezones which are listed as having fractions of hours in their offset,
      which appears to be due to changes in the offsets assigned to timezones since the author's version of Java to mine (Java 26), e.g.:

      • My machine says America/Caracas -04:00, but the example provided shows America/Caracas -04:30
      • My machine says Pacific/Norfolk +11:00, but the example provided shows Pacific/Norfolk +11:30

      That is because the article is using an out of date TZDB, whereas I am running Java 26 released this year with the latest TZDB.
      From the TZDB NEWS:

    liveuser@localhost-live:~$ wget https://data.iana.org/time-zones/releases/tzdata2026b.tar.gz  
    Saving 'tzdata2026b.tar.gz'
    HTTP response 200  [https://data.iana.org/time-zones/releases/tzdata2026b.tar.gz]
    tzdata2026b.tar.gz   100% [===========================================================>]  462.60K    --.-KB/s
                             [Files: 1  Bytes: 462.60K [133.27KB/s] Redirects: 0  Todo: 0 ]
    liveuser@localhost-live:~$ tar xvf tzdata2026b.tar.gz
    # [...]
    liveuser@localhost-live:~$ grep -E '(Caracas|Norfolk).*(switches|moves)' NEWS
       America/Caracas switches from -0430 to -04 on 2016-05-01 at 02:30.
       Norfolk moves from +1130 to +1100 on 2015-10-04 at 02:00 local time.
    

    See also e.g. https://web.archive.org/web/20151113142441/http://www.norfolkisland.gov.nf/nia/MediaRelease/Media%20Release%20Norfolk%20Island%20Standard%20Time%20Change.pdf (document is no longer hosted by Norfolk gov but is in web.archive.org ).

    Finally, there are also the differences in the output on recent versions of Java vs the tutorial author's version (unspecified) in the output for Tehran/Iran (I am seeing +03:30 instead of +04:30 , and I think Iran Daylight Savings Time is still discontinued since 2022; see https://en.wikipedia.org/w/index.php?title=Iran_Standard_Time&oldid=1349428906 ) and Asia/Rangoon has been renamed Asia/Yangon (but both entries have been kept by TZDB for backward compatibility).


    In the latest version of https://dev.java/learn/date-time/standard-calendar/ (archived as is at https://web.archive.org/web/20260604210444/https://dev.java/learn/date-time/standard-calendar/ )


    In the latest https://dev.java/learn/date-time/dayofweek-month/ (archived as is at https://web.archive.org/web/20260604215431/https://dev.java/learn/date-time/dayofweek-month/ )

    • In the sentences

      The Month enum includes constants for the twelve months, JANUARY through DECEMBER. As with the DayOfWeek enum, the Month enum is strongly typed, and the integer value of each constant corresponds to the ISO range from 1 (January) through 12 (December). Using the defined constants (Month.SEPTEMBER) makes your code more readable.

      the links for DayOfWeek and Month are broken (link to javadoc: which does nothing).


    Thanks so much!

  21. JosePaumard commented on Jun 5, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  22. willy-b commented on Jun 11, 2026

    @willy-b
    Author

    Thanks so much! Here are some additional items found during further review of the Date Time tutorials.


    In the latest version of https://dev.java/learn/date-time/dayofweek-month/ (archived as is at https://web.archive.org/web/20260609030620/https://dev.java/learn/date-time/dayofweek-month/ )

    • In the sentence

      As with the DayOfWeek enum, the Month enum is strongly typed, and the integer value of each constant corresponds to the ISO range from 1 (January) through 12 (December).

      the link on "Month" has not been fixed yet (on the page it still links to "javadoc:" and does nothing). There is an occurrence in a preceding sentence that has the link correct which might be why this one remained unresolved (because it is not broken in the previous sentence).


    In the latest version of https://dev.java/learn/date-time/zoneid-zone-offset/ (archived as is at https://web.archive.org/web/20260609214239/https://dev.java/learn/date-time/zoneid-zone-offset/ )

    • Thanks in advance for humoring this question: Why is TemporalAdjusters.lastInMonth() used to demonstrate OffsetDateTime? (It seems OffsetDateTime has no advantage over LocalDateTime for this)

      The following example uses OffsetDateTime with the TemporalAdjusters.lastInMonth() method to find the last Thursday in July 2013.

      The underlying question for me is: How can the time offset from UTC affect the answer to the question "what is the last Thursday in July 2013?" posed in this example. Indeed using -18:00 or +18:00 give the same answer (and those offsets span the maximum range of possible offsets for ZoneOffset , see the documentation at https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/ZoneOffset.html ).
      I assume there is likely a good motivating reason for this example but if it is unclear to me when the others are clear, I figure it is worth asking and it may be useful to add a sentence to motivate it for other readers.


    In the latest version of https://dev.java/learn/date-time/parsing-formatting/ (archived as is at https://web.archive.org/web/20260609222417/https://dev.java/learn/date-time/parsing-formatting/ )

    Image

    The output for this example, which prints both the arrival and departure time, is as follows:

    LEAVING:  Jul 20 2013  07:30 PM (America/Los_Angeles)
    ARRIVING: Jul 21 2013  10:20 PM (Asia/Tokyo)
    

    the part of the output which prints whether daylight savings is in effect is this time omitted
    ((Asia/Tokyo standard time will be in effect.))
    Where the output omitted is that corresponding to the code in the snippet which is as follows:

    if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant()))
     System.out.printf("  (%s daylight saving time will be in effect.)%n",
                       arrivingZone);
    else
     System.out.printf("  (%s standard time will be in effect.)%n",
                       arrivingZone);
    

    In the latest version of https://dev.java/learn/date-time/non-iso-conversion/ (archived as is at https://web.archive.org/web/20260610033131/https://dev.java/learn/date-time/non-iso-conversion/ )

    • In the sentence

      You can convert an ISO-based date to a date in another chronology by using the from(TemporalAccessor) factory method, such as JapaneseDate.from(TemporalAccessor).

      "JapaneseDate.from(TemporalAccessor)" is missing the fragment in its link URL to scroll to the appropriate method (missing "#from(java.time.temporal.TemporalAccessor)"),
      it links to https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/chrono/JapaneseDate.html when it should link to https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/chrono/JapaneseDate.html#from(java.time.temporal.TemporalAccessor) .

    • Nit: the "StringConverter" class is not described in text or declared in provided code blocks but used for invoking toString/fromString static methods in examples.
      One can determine implicitly that it is the unshown enclosing class but why not omit StringConverter and just call the methods as if the example blocks are in a static (e.g. main) method within the same class so they are in scope?

    • It may be worth noting explicitly that the expected input/output for toString/fromString are locale sensitive, e.g. the Buddhist Era era for ThaiBuddhist will be "BE" in one locale (Locale.US, TextStyle.SHORT) and "E. B." in another (Locale.FRENCH, TextStyle.SHORT), and apparently "B.E." for the dev.java author (unknown locale). This will cause exceptions if someone tries to test fromString in a different locale than the author.

    • Two possible problems with the static String toString(LocalDate localDate, Chronology chrono) example (beyond the input/output examples):
      First, the "pattern" variable is not referenced (not used in the DateTimeFormatter), which is inconsistent with the later fromString example.
      Second, Why does static String toString(LocalDate localDate, Chronology chrono) convert the LocalDate to a ChronoLocalDate when formatting with a DateTimeFormatter with the target Chronology?
      Per the documentation ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/format/DateTimeFormatter.html#withChronology(java.time.chrono.Chronology) ),

      When formatting, if the temporal object contains a date, then it will be converted to a date in the override chronology.

      thus, passing a ChronoLocalDate is unnecessary (and in fact the Chronology set in the ChronoLocalDate is ignored by the DateTimeFormatter which just uses its override Chronology, so it is misleading to set up a ChronoLocalDate in the code example).
      Note also that the ChronoLocalDate documentation ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/chrono/ChronoLocalDate.html )
      explains at length that using it should be generally avoided, preferring LocalDate:

      The design of the API encourages the use of LocalDate rather than this interface, even in the case where the application needs to deal with multiple calendar systems.
      [...]
      [Prefer instead to c]onvert the ISO LocalDate to and from the user's preferred calendar system during printing and parsing[.]

      Thus the existing code example could be simplified (marked up inline):

      public static String toString(LocalDate localDate, Chronology chrono) {
       if (localDate != null) {
           Locale locale = Locale.getDefault(Locale.Category.FORMAT);
           ChronoLocalDate cDate; // [ this should be removed ]
           if (chrono == null) {
               chrono = IsoChronology.INSTANCE;
           }
           try { // [ this should be removed]
               cDate = chrono.date(localDate); // [ this should be removed]
           } catch (DateTimeException ex) { // [ this should be removed]
               System.err.println(ex); // [ this should be removed]
               chrono = IsoChronology.INSTANCE; // [ this should be removed]
               cDate = localDate; // [ this should be removed]
           } // [ this should be removed]
           DateTimeFormatter dateFormatter =
               DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
                                .withLocale(locale)
                                .withChronology(chrono) // [<-- this sets the Chronology used in the output, the ChronoLocalDate chronology is ignored/irrelevant]
                                .withDecimalStyle(DecimalStyle.of(locale));
           String pattern = "M/d/yyyy GGGGG"; // [<-- already UNUSED which is inconsistent with the fromString example]
           return dateFormatter.format(cDate); // [<-- can just pass the localDate parameter here]
       } else {
           return "";
       }
      }
      

      The simplified (and less error-prone due to removing code that appears to set the Chronology (on ChronoLocalDate) which actually has no effect on the output) version is then:

      public static String toString(LocalDate localDate, Chronology chrono) {
       if (localDate != null) {
           Locale locale = Locale.getDefault(Locale.Category.FORMAT);
           if (chrono == null) {
               chrono = IsoChronology.INSTANCE;
           }
           DateTimeFormatter dateFormatter =
               DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
                                .withLocale(locale)
                                .withChronology(chrono)
                                .withDecimalStyle(DecimalStyle.of(locale));
      // unused pattern variable removed, does not change output since it was not used (maybe it was intended to be used but it would conflict with other DateTimeFormatter configuration above).
      // the fromString example should have its DateTimeFormatter also configured without the pattern the same as above for compatibility as toString is already not using the pattern (without changes from me)
           return dateFormatter.format(localDate);
       } else {
           return "";
       }
      }
      
    • In the subsequent example of LocalDate fromString(String text, Chronology chrono),
      aside from needing to fix the DateTimeFormatter to be consistent with toString not actually using the "pattern" variable,
      there is also some potentially unnecessary code: (commented on inline)

      TemporalAccessor temporal = df.parse(text);  // [ can just use `return LocalDate.parse(text, df)` here instead, but if not doing that, then... ]
      ChronoLocalDate cDate = chrono.date(temporal); // [this is still unnecessary...]
      return LocalDate.from(cDate); // [as LocalDate.from can take a TemporalAccessor directly, e.g. "temporal" can be passed here, removing the preceding line]
      

      so that it can be simplified to 1 line instead of 3, not using a ChronoLocalDate at all, just LocalDate.parse with the DateTimeFormatter which is Chronology aware:

      return LocalDate.parse(text, df);
      

    In the latest version of https://dev.java/learn/date-time/legacy-code/ (archived as is at https://web.archive.org/web/20260610203727/https://dev.java/learn/date-time/legacy-code/ )


    Thanks so much!

  23. willy-b commented on Jul 10, 2026

    @willy-b
    Author

    Also, in the latest version of https://dev.java/learn/date-time/parsing-formatting/ (archived as is at https://web.archive.org/web/20260710210105/https://dev.java/learn/date-time/parsing-formatting/?no_cache=true ) ,

    Thanks very much!

  24. JosePaumard commented on Jul 11, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.
    As for the OffsetDateTime example: I don't have any answer to this question. The example is correct, and it is an example in the OffsetDateTime section, so it makes sense to use this class. I need to find the author of the page and ask that person about that. It may take a while though, because this page has probably beed written some years ago.

  25. willy-b commented on Jul 18, 2026

    @willy-b
    Author

    Thanks so much! However, some of the updates made were not quite as requested and comments were not updated along with the code, so I give some additional clarifications and instructions below.


    In the latest version of https://dev.java/learn/date-time/parsing-formatting/ (archived as is at https://web.archive.org/web/20260718094137/https://dev.java/learn/date-time/parsing-formatting/?none=9 )

    • Re: the sentence

      However, if specify the format as MMM dd yyyy, with two characters for day of the month, then you would have to always use two characters, padding with a zero for a one-digit date: "Jun 03 2003".

      The update wasn't quite made as requested and seems to require a small additional update to be grammatical:
      "However, if specify ..." should be "However, if you specify" (there are other ways too, like "However, if one specifies" or "However, if specifying", etc).


    In the latest version of https://dev.java/learn/date-time/non-iso-conversion/ (arhived as is at https://web.archive.org/web/20260718094119/https://dev.java/learn/date-time/non-iso-conversion/?nonce=4 ),

    • toString/fromString code example comments mention code that was just removed by the dev.java team
      Thanks for the updates just made by the dev.java team,
      but it seems the comments in the code examples have not been updated, as they still mention using ChronoLocalDate(s).
      That is, (1) the toString comments mention a ChronoLocalDate conversion step which was removed (should not mention this):

      Converts a LocalDate (ISO) value to a ChronoLocalDate date
      using the provided Chronology, and then formats the
      ChronoLocalDate to a String using a DateTimeFormatter with a
      SHORT pattern based on the Chronology and the current Locale.

      and (2) the fromString comments are also out of date (mention the ChronoLocalDate intermediate step that was removed):

      Parses a String to a ChronoLocalDate using a DateTimeFormatter
      with a short pattern based on the current Locale and the
      provided Chronology, then converts this to a LocalDate (ISO)
      value.

    • Furthermore, the fromString code example still has unnecessary steps (see below) and uses a formatting pattern instead of relying only on builder parameters, inconsistent with toString

      String pattern = "M/d/yyyy GGGGG";
           DateTimeFormatter df = new DateTimeFormatterBuilder()
                 .appendPattern(pattern)
                 .toFormatter(locale)
                 .withChronology(chrono)
                 .withDecimalStyle(DecimalStyle.of(locale));
           TemporalAccessor temporal = df.parse(text);
           return LocalDate.from(temporal, df);
      

      This should NOT convert from text to TemporalAccessor, then to LocalDate, using DateTimeFormatter 2x instead of 1x
      (simpler would be just to use return LocalDate.parse(text, df);).

      Also, the fromString example should not use a pattern string when the toString method does not use one, and should configure the DateTimeFormatter exactly the same for fromString as for toString.
      (And as mentioned above separately, the comment above fromString is also out of date, mentioning ChronoLocalDate which is no longer used.)


    In the latest version of https://dev.java/learn/date-time/legacy-code/ (archived as is at https://web.archive.org/web/20260718095302/https://dev.java/learn/date-time/legacy-code/?nonce=15 ),

    (this was mentioned earlier but has not been fixed so I mention it again)


    Thanks!

  26. JosePaumard commented on Jul 20, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  27. willy-b commented on Jul 30, 2026

    @willy-b
    Author

    Thanks for the updates, the items reported above have been resolved.


    In the latest version of https://dev.java/learn/date-time/temporal/ (archived as is at https://web.archive.org/web/20260730074408/https://dev.java/learn/date-time/temporal/?nonce=1 )

    • In the code example

      // define a year, a month and a day
      LocalDate = date = LocalDate.of(year, month, day);
      boolean isFamilyVacation = date.query(new FamilyVacations());
      

      LocalDate = date should be LocalDate date.


    On a separate note, in the upstream Javadocs (not dev.java but maybe you can share this with the appropriate team), on https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/TemporalField.html#adjustInto(R,long)
    for TemporalField.adjustInto(R temporal, long newValue), there appears to be an error in their provided code example:

    // these two lines are equivalent, but the second approach is recommended
    temporal = thisField.adjustInto(temporal);
    temporal = temporal.with(thisField);
    

    The example uses a non-existent one-argument overload of TemporalField.adjustInto (there is only the two argument form TemporalField.adjustInto(Temporal,long) ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/TemporalField.html#adjustInto(R,long) ); the example seems confused with TemporalAdjuster which has a same name method with one argument, at TemporalAdjuster.adjustInto(java.time.temporal.Temporal) ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/TemporalAdjuster.html#adjustInto(java.time.temporal.Temporal) ))

    The first line of the example should be temporal = thisField.adjustInto(temporal, someLongValue);.for some long someLongValue.

    The second line of the example above also seems to confuse
    Temporal.with(java.time.temporal.TemporalField,long)( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/Temporal.html#with(java.time.temporal.TemporalField,long) )
    with a method accepting a TemporalAdjuster instead of a TemporalField, Temporal.with(java.time.temporal.TemporalAdjuster) ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/Temporal.html#with(java.time.temporal.TemporalAdjuster) ) .

    So the second line should be temporal = temporal.with(thisField, someLongValue); (using https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/time/temporal/Temporal.html#with(java.time.temporal.TemporalField,long) ).

    Annotated image:
    Image

    Thanks!

  28. JosePaumard commented on Jul 31, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  29. willy-b commented on Aug 5, 2026

    @willy-b
    Author

    Thanks! Here are a few more items that came up during additional review of this material.


    In the latest version of https://dev.java/learn/reflection/constructors/ (archived as is at https://web.archive.org/web/20260804200244/https://dev.java/learn/reflection/constructors/?nonce=2 )

    • In the sentence

      A Constructor object lets you get more information on the corresponding constructor: its modifiers, the types and names of its parameters,
      and enables you to invoke it to create instance of objects, passing the arguments you need.

      "to create instance of" should be either "to create an instance of" (with singular "an object" or "a class") or "to create instances of" (with plural "objects").

    • In the sentence (this one is trivial, but may be of interest to fix)

      IllegalAccessException: in case the no-arg constructor is not accessible from the calling code. This is the case if the constructor is private, and is accessed from another class.

      it may be clearer to replace "no-arg constructor" with "constructor" because Constructor.newInstance(Object... initargs) can throw an IllegalAccessException regardless of whether the constructor invoked is no-arg,
      even though the particular code example here happens to invoke a no-arg constructor (and of String.)


    In the latest version of https://dev.java/learn/reflection/arrays/ (archived as is at https://web.archive.org/web/20260805044605/https://dev.java/learn/reflection/arrays/ )


    In the latest version of https://dev.java/learn/date-time/non-iso-conversion/ (archived as is at https://web.archive.org/web/20260801202426/https://dev.java/learn/date-time/non-iso-conversion/?nonce=10 )

    • In the fromString code example

        return LocalDate.parse(text, dateFormatter);    }
      

      the ending curly brace is on the same line when it should be on a new line to be consistent with the rest of the code example.


    In the latest version of https://dev.java/learn/date-time/legacy-code/ (archived as is at https://web.archive.org/web/20260801204511/https://dev.java/learn/date-time/legacy-code/?nonce=11 )

    • In the sentence

      The ZoneId class specifies a time zone identifier and has access to the rules used each time zone.

      "to the rules used each time zone" should be "to the rules used by each time zone" (missing "by").


    Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions