diff --git a/notes/java.md b/notes/java.md index 15be6a6..1399df5 100644 --- a/notes/java.md +++ b/notes/java.md @@ -1,9 +1,6 @@ # Recommendations for solving lab 2 with Java -The stub for lab 2 uses Java 21, -which admits a more readable alternative to the visitor pattern suggested in the book. -The following example code shows how you can process an object ```exp``` of the type type ```Exp``` -generated by the parser defined in the stub. +The stub for lab 2 uses Java 21, which admits a more readable alternative to the visitor pattern suggested in the book. The following example code shows how you can process an object ```exp``` of the type ```Exp``` generated by the parser defined in the stub. ```java switch(exp) { @@ -16,8 +13,7 @@ switch(exp) { } ``` -On the right hand side of the ```->``` you can extract further information from ```e```, -for example, in the case ```EAnd e```, you can use the two operands ```e.exp_1``` and ```e.exp_2```. +On the right hand side of the ```->``` you can extract further information from ```e```, for example, in the case ```EAnd e```, you can use the two operands ```e.exp_1``` and ```e.exp_2```. ```switch```, if used like above produces a Java-expression, which means that you can use it in assignments: @@ -25,18 +21,17 @@ for example, in the case ```EAnd e```, you can use the two operands ```e.exp_1`` var typedExpression = switch(exp) { ... } ``` -To define the neccessary datatypes, ```record```s are very useful. -You can define a record like this: +To define the neccessary datatypes, ```record```s are very useful. You can define a record like this: ```java -public record TypedAnd(TypedExpr e1, TypedExpr e2) { +record TypedAnd(TypedExpr e1, TypedExpr e2) { } ``` Objects of this type can be constructed with ```new TypedAnd(e1,e2)``` where ```e1``` and ```e2``` are ```TypedExpr```s. For solving the lab, it is reasonable to define an ```interface``` ```TypedExpr``` with a method which returns the type of an expression: ```java -public interface TypedExpr { +interface TypedExpr { Type type(); } ``` @@ -44,9 +39,34 @@ public interface TypedExpr { Where ```Type``` is a datatype for types that needs to be defined. With this interface, the ```TypedAnd``` from above can be turned into an implementation like this: ```java -public record TypedAnd(TypedExpr e1, TypedExpr e2) { +record TypedAnd(TypedExpr e1, TypedExpr e2) { Type type() { return new Type.Bool; // this is what it could look like if you implement basic types as an enum } } ``` + +To keep a good overview, you can write implentations of an interface directly into the interface as "inner classes": + +```java +interface TypedExpr { + Type type(); + + record And(TypedExpr e1, TypedExpr e2) implements TypedExpr { + Type type() { + return new Type.Bool; + } + + record Var(String name, Type type) implements TypedExpr { } +} +``` + +The second record `Var` shows a handy shortcut to overriding the method `type`: if the record has an attribute of the right Type and name ("`type`" in this case), it will automotically become the implementation. + +You can work with a variable of type `TypedExpr` like in the example above, by using `switch` expressions. If you want to avoid the `default` case, you can use a `sealed interface` instead. To make that work, you have to declare all implementations using `permits` like this: + +```java +sealed interface TypedExpr permits And, Var { +... +} +```