1. Records, Interfaces, Enumerations, Sealed Classes
1.1. Records
Records are useful in Java because they provide a simple and compact way to define and use data objects with a fixed number of attributes.
1.1.1. Quiz: Which statements are true for Records? ⭐
Given the following Java Record:
record Candy( String name, int calories ) { }
Which of the following statements is true?
The name and age of a
candy
cannot be changed after the object is created.The attributes of a
Candy
object are accessed via getter methods.Candy
cannot have static methods.The
equals()
andhashCode()
methods must be implemented manually.The
toString()
method ofCandy
returns a JSON-like string.
1.1.2. Quiz: Records with static variables ⭐
Given the following Java Record:
record Candy( String name, int calories ) {
public static String id = UUID.randomUUID().toString();
}
Does the record compile?
Is the following a valid alternative notation?
record Candy( String name, int calories ) {
public static String id;
Candy {
id = UUID.randomUUID().toString();
}
}
1.1.3. Record Patterns (NEW) ⭐
Daredevil pirates not only have a keen sense of prey, but also a fondness for exotic pets. So far, one application represents pets in two records:
record MischiefMonkey( String name, boolean isMutinous ) { }
record FeistyParrot( String name, String favoritePhrase, boolean isMutinous ) { }
The modeling has proven to be rigid as new animal types are added and properties change. Therefore, the data from the records should be converted into java.util.Properties
objects. Properties
is a special associative store that associates one string with another string. The method setProperty(String key, String value)
sets a new key-value pair into the Properties
object.
Task:
Write a method
Properties convertToProperties(Object)
to convert pets of typeMischiefMonkey
andFeistyParrot
intoProperties
objects.Pets that are mutinous (state
isMutinous
) should be ignored and result in an emptyProperties
object with no entries.The method should be able to be called with
null
or unknown types and return an emptyProperties
object in those cases.
Examples (output shows the toString()
representation of Properties
):
new MischiefMonkey( "Jack", true )
→{}
new FeistyParrot( "Captain Squawk", "Avast, ye scallywags!", false )
→{favoritePhrase=Avast, ye scallywags!, name=Captain Squawk}
new MischiefMonkey( "Barbossa", false )
→{name=Barbossa}
new FeistyParrot( "Polly", "Pieces of eight!", true )
→{}
new FeistyParrot( "Marauder", "Walk the plank!", false )
→{favoritePhrase=Walk the plank!, name=Marauder}
1.2. Interfaces
Abstract classes are still classes with all their capabilities: instance variables, constructors, methods, different visibilities. Often a simpler form of specification is sufficient, and for this Java provides interfaces. They do not have instance variables, but can have constants, abstract methods, static methods, and default
methods—an instance variable is a way of storing something that belongs to a class, not to the interface.
1.2.1. Compare consumption of electrical devices ⭐
Every electrical device has a power output, which is measured in watts.
Task Part 1:
Declare in
ElectronicDevice
a privateint
instance variablewatt
, and generate with the development environment Setter/Getter.Add a
toString()
method that returns something like the following:"ElectronicDevice[watt=12kW]"
. Some subclasses had already overriddentoString()
; they should then include asuper.toString()
in theirtoString()
methods.
Task Part 2:
Write a new class
ElectronicDeviceWattComparator
that implements the interfacejava.util.Comparator<ElectronicDevice>
.Let the
compare(…)
method define an ordering of electrical devices, where an electrical device is "smaller" if it consumes less power.Put a
println(…)
in your owncompare(…)
method for a better understanding, so that you can see which objects are compared.
Example:
ElectronicDevice ea1 = new Radio(); ea1.setWatt( 200 );
ElectronicDevice ea2 = new Radio(); ea2.setWatt( 20 );
Comparator<ElectronicDevice> c = new ElectronicDeviceWattComparator();
System.out.println( c.compare(ea1, ea2) );
System.out.println( c.compare(ea2, ea1) );
Objective of the exercise: ElectronicDeviceWattComparator
as an implementation of the Comparator
interface, as shown in the UML diagram:
1.2.2. Find electronic devices with the highest power consumption ⭐
The java.util.Collections
class has a static method that returns the largest element of a collection (the generics in the angle brackets have been removed for simplicity):
static T max( Collection coll, Comparator comp )
The following must therefore be passed to the max(…)
method
a
Collection
implementation likeArrayList
anda
Comparator
implementation. Here we can use ourElectronicDeviceWattComparator
.
Task:
Put a method
findMostPowerConsumingElectronicDevice()
in the ship that returns the device with the highest consumption.
Example:
The following program returns the output
12000
.Radio grannysRadio = new Radio(); grannysRadio.volumeUp(); grannysRadio.setWatt( 12_000 ); TV grandpasTv = new TV(); grandpasTv.setWatt( 1000 ); Ship ship = new Ship(); ship.load( grannysRadio ); ship.load( grandpasTv ); System.out.println( ship.findMostPowerConsumingElectronicDevice().getWatt() );
1.2.3. Use Comparator interface for sorting ⭐
If you want to sort objects of a list, you can use the sort(…)
method on List
implementations. It is important to tell the sort(…)
method when one object is "smaller" than another. For this purpose, our ElectronicDeviceWattComparator
can be used; it is a prerequisite for objects that you want to sort — the signature void sort(Comparator<…> c)
already reveals this.
Task:
Call
sort(…)
inload(…)
of theship
object after adding it to its data structure, to always have an internal sorted list after adding.
1.2.4. Static and default methods in interfaces ⭐⭐⭐
Interfaces can contain static methods and serve as factory methods, that is, they can provide instances of classes that implement that interface.
Task:
Create an interface
Distance
.Set two static methods
Distance ofMeter(int value)
andDistance ofKilometer(int value)
inDistance
, which return a new object of typeDistance
.Set in
Distance
an abstract methodint meter()
. What must be implemented?Set a default method
int kilometer()
in the interfaceDistance
.
Example in use:
Distance oneKm = Distance.ofKilometer( 1 );
System.out.printf( "1 km = %d km, %d m%n", oneKm.kilometer(), oneKm.meter() );
Distance moreMeter = Distance.ofMeter( 12345 );
System.out.printf( "12345 m = %d km, %d m", moreMeter.kilometer(), moreMeter.meter() );
1.2.5. Delete selected elements with Predicate ⭐⭐
If we want to make ships energy efficient, we need to remove all devices with excessive power consumption.
The List
method removeIf(Predicate<…>filter)
deletes all elements that satisfy a predicate. The ArrayList
class is an implementation of the List
interface, so the method is available on an ArrayList
.
For example, if we want to delete from a List<String>
all empty strings, we can call removeIf(new IsStringEmpty())
on the list, where IsStringEmpty
is declared as follows:
class IsStringEmpty implements Predicate<String> {
@Override public boolean test( String t ) {
return t.trim().isEmpty();
}
}
Task:
Set in the ship a new method
removePowerConsumingElectronicDevices()
that deletes all devices with power consumption greater than a self-selected constantMAXIMUM_POWER_COMSUMPTION
.
1.3. Enumeration types (enum)
Enumeration types (enum
) represent closed sets and are powerful in Java; they not only allow additional object and class variables, new private constructors, but they can also implement interfaces, override methods, and they have some built-in methods. The upcoming exercises address these nice features.
1.3.1. Enumeration for candy ⭐
Captain CiaoCiao wants to appeal to a younger crowd of buyers and is experimenting with candy instead of rum in his lab.
Task:
Declare an enumeration
CandyType
with constants forcaramels
chocolate
gummies
licorice
lollipops
Chewing Gums
Cotton Candy
Respect the usual naming convention.
Users should be able to enter a candy from the console. The appropriate
enum
object should be retrieved for the input, case-insensitivity should not matter. Add a new static methodOptional<CandyType> fromName(String input)
in the enumeration typeCandyType
for the conversion from aString
to an enumeration element of typeCandyType
. The method must not throw exceptions due to incorrect input; unknown names will result in anOptional.empty()
.
1.3.2. Deliver random candies ⭐
Captain CiaoCiao launches his food tour and always chooses random candies.
Task:
Give the enumeration type
CandyType
a methodrandom()
that returns a random candy.System.out.println( CandyType.random() ); // e.g., CHOCOLATE System.out.println( CandyType.random() ); // e.g., LOLLIPOPS
1.3.3. Tagging candy with addictive value ⭐⭐
We know that sweets are addictive, some more, some less.
Task:
Associate an addictive value (
int
) with each enumerated element fromCandyType
:Caramels: 9
Chocolate: 5
gummies: 4
Licorice: 3
Lollipops: 2
Chewing Gums: 3
Cotton Candy: 1
To store the addiction value, use a constructor in
enum
. The addiction value should be provided by a new non-static methodaddictiveQuality()
.Since Captain CiaoCiao wants to achieve a dependency towards candy with a higher addiction factor, a new
CandyType
methodnext()
shall return the candy with the next higher addiction. Lollipops have two potential successors, here the selection shall randomly go to Chewing Gums and Licorice. Caramels have nosuccessor
, and it stays with Caramels.
Examples:
CandyType.COTTON_CANDY.next()
isLOLLIPOPS
.CandyType.LOLLIPOPS.next()
is e.g.LICORICE
.CandyType.LOLLIPOPS.next()
is e.g.CHEWING_GUMS
.CandyType.CARAMELS.next()
isCARAMELS
.
1.3.4. Interface implementations via an enum ⭐⭐
An enum type can implement interfaces, but cannot extend classes.
Given an interface Distance
:
interface Distance {
double distance( double x1, double y1, double x2, double y2 );
double distance( double x1, double y1, double z1, double x2, double y2, double z2 );
}
Task:
Take the interface
Distance
into your project.Declare an enumeration type
Distances
that implementsDistance
with exactly one enumeration elementEUCLIDEAN
:enum Distances implements Distance { EUCLIDEAN }
If you now need a
Distance
implementation for the Euclidean distance, you can get it usingDistances.EUCLIDEAN
.Add the implementation that the Euclidean distance of two points is calculated; remember, for a 2D point:
Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) )
Extend the enumeration type
Distances
with another enumeration elementMANHATTAN
so that there are two constantsEUCLIDEAN
andMANHATTAN
.
The Manhattan distance is formed by the sum of the absolute differences of the single coordinates, so for a 2D pointMath.abs(x1 - x2) + Math.abs(y1 - y2)
.