Objectos Weekly #005: Java's contextual keywords, class names and method names
Tags: java, weeklyretro
Published: 2022-12-17 12:57:13

Welcome to Objectos Weekly issue #005.

This is the first issue sent via the email newsletter. If you have received this issue in your email and had any troubles opening it, I apologize in advance. I would really appreciate it if you sent me a few words explaining what went wrong. And thank you in advance.

Please know that I will be open for Java freelancing work in 2023. You can find my contacts on [this page](https://www.objectos.com.br/about/marcio.html). All work will be provided via Objectos Software LTDA based in São Paulo, Brazil.

## Contextual keywords

Java 9 introduced the concept of [restricted keywords](https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.9). Since Java 17 they are called [contextual keywords](https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-ContextualKeyword).

Contextual keywords allow for the same token to be interpreted as either a keyword or as a different token, an identifier typically. It depends on where the token appears in a compilation unit.

### Reserved keywords

To understand contextual keywords, let's first discuss reserved keywords.

In Java, identifiers cannot have the same character sequence of a reserved keyword. You cannot name a method `try` because `try` is a reserved keyword.

```java
// does not compile!
// 'try' is a reserved keyword
public void try() {
  ...
}
```

The same rule applies to other declarations having one or more identifiers. Examples include package names, module names, field names, local variable names, class names and others.

### Backward compatibility

Reserved keywords represent a problem when evolving the language. As an example, let's take the introduction of records in Java 14.

Suppose that you are responsible for an application that currently targets Java 11. It contains a class called `Record`. So _spread throughout your codebase_ you have many fields, methods and local variables named `record`:

```java
class Foo {
  private Record record;
  
  public static Record of(int value) {
    var record = Record.create();
    record.set(value);
    return record;
  }

  public Record record() {
    return record;
  }
}
```

Suppose now that you want to upgrade to Java 17.

If the language designers decided to make `record` a reserved keyword, your application would not compile anymore.

So they made `record` a contextual keyword instead.

### List of contextual keywords

As of Java 19, the [contextual keywords](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-ContextualKeyword) are the following:

```
exports      opens      requires     uses
module       permits    sealed       var
non-sealed   provides   to           with
open         record     transitive   yield
```

We can use them in _almost_ any place an identifier is required. As an example, consider the following:

```java
package post.yield;

public class Contextual {
  public static void main(String[] uses) {
    var var = "Contextual keywords example";

    exports(var);
  }

  private static void exports(String record) {
    System.out.println(record);
  }
}
```

The Java program above uses contextual keywords:

- `yield` in the package name;
- `uses` in the main method parameter name;
- `var` as the name of the local variable;
- `exports` as the name of the private method; and
- `record` as the name of the parameter of the private method.

The program compiles and runs. It prints:

```
Contextual keywords example
```

## Contextual keywords and class names

In the last section I emphasized that contextual keywords can be used in _almost_ any place an identifier is required.

A place where a few of the contextual keywords are restricted is _class names_. To be precise, by 'class name' I mean the name of any _type declaration_, i.e, a class, an interface (annotation included), an enum or a record.

The following jshell session illustrates all of the contextual keywords that are not allowed as class names. I have edited the output slightly so it is easier to read:

```
jshell> interface permits {}
|  Error:
|  'permits' not allowed here
|    as of release 15, 'permits' 
|      is a restricted type name 
|      and cannot be used for type declarations
|  interface permits {}
|            ^

jshell> record record(int value){}
|  Error:
|  'record' not allowed here
|    as of release 14, 'record' 
|      is a restricted type name 
|      and cannot be used for type declarations
|  record record(int value){}
|         ^

jshell> enum sealed {INSTANCE;}
|  Error:
|  'sealed' not allowed here
|    as of release 15, 'sealed'
|      is a restricted type name 
|      and cannot be used for type declarations
|  enum sealed {INSTANCE;}
|       ^

jshell> class var {}
|  Error:
|  'var' not allowed here
|    as of release 10, 'var'
|      is a restricted type name 
|      and cannot be used for type declarations
|  class var {}
|        ^

jshell> @interface yield {}
|  Error:
|  'yield' not allowed here
|    as of release 14, 'yield'
|      is a restricted type name 
|      and cannot be used for type declarations
|  @interface yield {}
|             ^
```

So the following contextual keywords are not allowed: `permits`, `record`, `sealed`, `var` and `yield`. 

As usual, this is defined by the [JLS section 3.8](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-TypeIdentifier). It defines the production `TypeIdentifier`:

```
TypeIdentifier:
  Identifier but not permits, record, sealed, var, or yield
```

So class names can be any identifier _except_ those five contextual keywords.

Let's try with contextual keywords not listed in the `TypeIdentifier` production:

```
jshell> class opens {}
|  created class opens

jshell> class exports {}
|  created class exports
```

Other keywords work.

## Contextual keywords and method names

Method names do not suffer from the same restriction of class names.

However, if you name a method `yield` you must observe one thing. The following class _does not_ compile:

```java
// does not compile!
public class YieldMethod {
  public void callYield() {
    yield(); // <-- compilation error here
  }

  void yield() {
    System.out.println("yield() called!");
  }
}
```

The problem is in the method `callYield`. Defining the class above in jshell causes the following error message:

```
|  Error:
|  invalid use of a restricted identifier 'yield'
|    (to invoke a method called yield, 
|        qualify the yield with a receiver or type name)
|      yield();
|      ^---^
```

So, to fix the compilation error, we must rewrite the `callYield` method to:

```java
public void callYield() {
  this.yield();
}
```

Let's test this fix using jshell:

```
jshell> public class YieldMethod {
   ...>   public void callYield() {
   ...>     this.yield();
   ...>   }
   ...> 
   ...>   void yield() {
   ...>     System.out.println("yield() called!");
   ...>   }
   ...> }
   ...> 
|  created class YieldMethod

jshell> new YieldMethod().callYield();
yield() called!
```

### Why though?

I don't know the reason why. But, as usual, this is defined in the Java Language Specification.

[Section 15.12](https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.12) defines method invocation expressions:

```
MethodInvocation:
  MethodName ( [ArgumentList] )
  TypeName . [TypeArguments] Identifier ( [ArgumentList] )
  ExpressionName . [TypeArguments] Identifier ( [ArgumentList] )
  Primary . [TypeArguments] Identifier ( [ArgumentList] )
  super . [TypeArguments] Identifier ( [ArgumentList] )
  TypeName . super . [TypeArguments] Identifier ( [ArgumentList] )
```

Notice that, except for the first form, the method name is given by an `Identifier` token. The first is the form of the unqualified method invocation. In the unqualified form the method name is given by:

```
MethodName:
  UnqualifiedMethodIdentifier

UnqualifiedMethodIdentifier:
  Identifier but not yield
```

So you _cannot_ invoke a method named `yield` using the unqualified form.

This is the reason why, in our previous example, we had to _qualify_ the `yield` method invocation.  

## Until the next issue of Objectos Weekly

So that's it for today. I hope you enjoyed reading.

The source code of all of the examples are in this [GitHub repository](https://github.com/objectos/blog-examples/tree/master/p025-ow-005/src/main/java/post/yield). 

_P.S. Thank you Dr. Heinz M. Kabutz for the encouragement to start the newsletter_