MPS 2021.3 Help

MPS Java compatibility

Configuration

The Java Compiler configuration tab in the preferences window only holds a single setting - Project bytecode version.

Screen shot 2015 01 30 at 17 20 40

This setting defines the bytecode version of all Java classes compiled by MPS. These classes include classes generated from languages aspects, classes of the runtime solutions, classes of the sandbox solutions, and so on.

By default, the bytecode version is set to JDK Default. This means that the version of the compiled classes will be equal to the version of Java, which MPS is running under. For example, if you run MPS under JDK 1.8 and JDK Default is selected, the bytecode version will be 1.8.

Each module can specify the Java level in its module properties on the Java tab. This way only the features of the chosen language level will be enabled in BaseLanguage code of that module.

Java compatibility

Build scripts

Also, dont forget to set java compliance level in the build scripts of your project. It should be the same as the project bytecode version.

Screen shot 2015 01 29 at 18 08 28

Using java classes compiled with JDK 1.8

In the MPS modules pool you can find the JDK solution, which holds the classes of the running Java. So when you start MPS under JDK 1.8, the latest Java Platform classes will be available in the JDK solution.

You can also use any external Java classes, compiled under JDK 1.8 by adding them as Java stubs.

Default interface methods

Java 8 introduced also default methods. These are methods implemented directly in the interface. You can read about default methods here: http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

These methods can be called just like the usual instance methods. Sometimes, however, you need to call the default method directly from an interface that your class is implementing. For example, in case of multiple inheritance when a class implements several interfaces, each containing a default method with the same signature.

public interface A {   public default void foo() {     System.out.println("A");    } } public interface B {   public default void foo() {     System.out.println("B");    } }

In that case foo() can be called explicitly on one of the interfaces via a SuperInterfaceMethodCall construction, which is newly located in the jetbrains.mps.baseLanguage.jdk8 language.

Screen shot 2015 01 29 at 20 39 29

The jetbrains.mps.baseLanguage.jdk8 language allows users to create the 'default' methods in BaseLanguage interfaces.

Comx1

The 'default' keyword is implemented by the DefaultModifier concept which extends the Modifier concept in the BaseLanguage.

The transformation/substitute menu + some cosy intentions are also supplied.

Comx2
Comx3

Using Java platform API

Java 8 introduced lambda expressions, of which you can learn more here: http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html.
MPS provides its own closure language, which is compatible with the new Java API.

Here is the example of an interaction with the new JDK 8 Collections API:

Screen shot 2015 01 29 at 18 03 43

The forEach() method is the new default method of java.lang.Iterable. It takes a Consumer interface as a parameter. Consumer is a functional interface as it only has one method. In Java 8 it would be possible to pass a lambda expression to forEach(). In MPS you can pass the MPS closure. A closure knows the type of the parameter taken by forEach() while generating and it will be generated exactly to the correct instance of the Consumer.

The MPS closures are by default generated into Java classes, however, if a closure meets four compatibility rules, MPS will generate it into a Java lambda expression. Incompatibilities include the usage of:

  • Yield operations

  • “Functional” abstract classes

  • Annotations

  • Local variables conflicting with parent scope

Lambdas0 5


Lambda1

Some issues may arise from this change:

  • Ambiguous calls: while undetected in MPS, the Java compiler will report such issues with closures generated as lambdas.

  • Closure instances: as a lambda, a closure will create a single instance at runtime, as opposed to anonymous classes that create a new instance every time they are called (see screenshot below).

Last modified: 10 January 2022