This FAQ is to help you based on the most commonly asked questions we have seen in the workshops and drop-ins. The full specification of the functional Java is still available here and it covers all aspects of the language.
Nested switch – Not Allowed#
Say we wanted a function to tell whether a car should go. We need to know if the traffic light is green and there is no car in front
boolean shouldCarGo(Colour colour, boolean carInFront){
switch (colour) {
case Green:
switch(carInFront) {
case true:
return false;
case false:
return true;
}
case Orange:
return false;
case Red:
return false;
}
}
This is not allowed, as we have a switch statement inside another statement. While this logic can be simplified, if we want the following logic we can use a helper function:
boolean shouldCarGo(Colour colour, boolean carInFront){
switch (colour) {
case Green:
return canGo(carInFront);
case Orange:
return false;
case Red:
return false;
}
}
boolean canGo(boolean carInFront) {
switch (carInFront) {
case true:
return false;
case false:
return true;
}
}
Nested If statement – Not Allowed#
Say we wanted to determine which floor an elevator should go to we cannot use two nested if statements to determine if the floor is not the current floor and there is a person inside.
int goToFloor(boolean hasPassanger, int selectedFloor, int currentFloor) {
if (hasPassanger) {
if (floor != currentFloor) {
return selectedFloor;
}
}
return currentfloor;
}
This is not allowed as it uses nested if statements.
Changing variable – Not Allowed#
Say we have a function where we want to change the water level based on high or low tide:
int calculateTide(int currentTide, booleean high) {
if (high) {
currentTide += 50;
} else {
currentTide -= 50;
}
return currentTide;
}
This is not allowed, as we cannot change the value of a variable once it is assigned. We can either create a new variable or just return the current value.
int calculateTide(int currentTide, booleean high) {
if (high) {
return currentTide + 50;
} else {
return currentTide - 50;
}
}
Bad examples – Not Allowed#
When making a data definition, the examples should show possible specific examples of what the data type could be:
/**
* examples:
* Fox(id, name, the weight, red or yellow)
*/
record Fox(int id, String name, int weight, Colour colour) {}
The above is not allowed and they should be useful and specific as shown below
/**
* examples:
* Fox(1, "Frank", 30, Orange): a fox with id 1, name Frank, weight 30 and colour Orange.
*/
record Fox(int id, String name, int weight, Colour colour) {}
If expression within switch – Allowed#
This is allowed, an example is shown below
boolean shouldCarGo(Colour colour, boolean carInFront){
switch (colour) {
case Green:
return (carInFront ? false : true);
case Orange:
return false;
case Red:
return false;
}
}
Example of a function that follows the design recipe
Several examples of what you cannot do have been shown, so here is an example that follows the design recipe:
We will show an example where we have foxes, rabbits and grass. The foxes eat the rabbits and the rabbits eat the grass.
Data design
/**
* A fox is one of the organism. It eats rabbits in our
* simulation.
* examples:
* Fox(1): a fox with id 1
* @param id the id number
*/
record Fox(int id) {}
// {... fox.id() ...}
/**
* A rabbit is one of the organism. It eats grass in our
* simulation.
* examples:
* Rabbit(13): a Rabbit with id 13
* @param id the id number
*/
record Rabbit(int id) {}
// {... rabbit.id() ...}
/**
* Grass is one of the organism. It eats nothing in our
* simulation. The height determins whether it can be
* eaten by a rabbit.
* examples:
* Grass(4, 3): Grass with id 4 and a height of 3
* @param id the id number
*/
record Grass(int id, int height) {}
// {... grass.id() ... grass.height() ...}
/**
* An organism in our simulation can either be a:
* Fox - eats rabbits
* Rabbit - eats grass
* Grass - grows automatically and is eaten by rabbits
*/
sealed interface Organism permits
Fox, Rabbit, Grass {}
// Template:
// {
// ...
// return ... switch(organism) {
// case Grass(var id, var height) -> ...;
// case Rabbit(var id) -> ...;
// case Fox(var id) -> ...;
// } ...;
// }
Now that we have designed our data we can write a function. We want to know if an organism can eat another
/**
* This function determines if the eater can eat the eaten organism, return true if so, false otherwise.
* Examples:
* canEat(Rabit,Grass) return true.
* canEat(Fox,Rabbit) returns true.
* canEat(Grass,Fox) returns false.
* canEat(rabbit,Fox) returns false.
* Design Strategy:
* Template application
* @param eater the organism trying to eat the other
* @param eaten the organism that the eater is trying to eat.
* @return true if the first organism can eat the second, false otherwise.
*/
boolean canEat(Organism eater, Organism eaten) {
return switch (eater) {
case Grass(id, height) -> false;
case Rabbit(id) -> canEatRabbitHelper(eaten);
case Fox(id) -> canEatFoxHelper(eaten);
}
}
Now we can write two helper functions, we need to repeat the design recipe for these two functions. Here is the rabbit function
/**
* This function determines if a rabbit could eat the organism
* Examples:
* canEatRabbitHelper(Rabbit(3)) returns false
* canEatRabbitHelper(Fox(4)) returns false
* canEatRabbitHelper(Grass(5,5)) returns true
* canEatRabbitHelper(Grass(5,0)) returns false
* Design strategy: template application
* @param eaten the organism the rabbit is trying to eat
* @return true if there is grass and its height is more than 0
*/
boolean canEatRabbitHelper(eaten) {
return switch (eaten) {
case Grass(id, height) -> height > 0;
case Rabbit(id) -> false;
case Fox(id) -> false;
}
}
Here are common issues we observed on Assignment 1. Keep in mind they are not exhaustive, e.g., there are other libraries, data-structures, keywords, etc that are not allowed in functional Java and Assignment 2 that are not mentioned here. For the full specification of the functional Java see the documentation.
instanceof Operator – Not Allowed#
// Non-compliant: Uses the instanceof operator
boolean isValuable(AnalysisResult r) {
return r instanceof Valuable; // VIOLATION
}
Use switch expressions and pattern matching instead.
for, while and class – Not Allowed#
The following keywords cannot be used in the functional JAVA assignments: for, while and class
Type Casting – Not Allowed#
Explicit type casting, such as (StarChart) ownedArtifact, is not allowed.
java.util.Random – Not Allowed#
// Non-compliant
import java.util.Random;
// ...
public static AnalysisResult riskTakerAnalysisInertRock(Artifact owned,Artifact newArtifact) {
// ...
if (Math.random() < 0.5) { // VIOLATION: Should use RandomNumber()
return AnalysisResult.VALUABLE;
}
// ...
}
java.util.List – Not Allowed#
// Non-compliant: Uses java.util.List and its methods
import java.util.List;
List cardinalDirections = List.of( // VIOLATION
Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST
);
Direction randomCardinalDirection(){
return cardinalDirections.get(RandomNumber(0, cardinalDirections.size())); // VIOLATION
}
Use the provided ConsList instead.