-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Generics / Code snippets |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
class Box<T> { | ||
private T value; | ||
|
||
public void setValue(T value) { | ||
this.value = value; | ||
} | ||
} | ||
|
||
class BoxOfInt extends Box<Integer> { | ||
private Integer value; | ||
|
||
@Override | ||
public void setValue(Integer value) { | ||
this.value = value; | ||
} | ||
|
||
/* | ||
After type parameter erasure, the overriden setValue() method in the superclass and here | ||
would have different signatures: | ||
public void setValue(Object value) { this.value = value; } // in Box | ||
public void setValue(Integer value) { this.value = value; } // in BoxOfInt | ||
To solve this, the compiler generates a synthetic method in BoxOfInt, called a "bridge method": | ||
public void setValue(Object value) { setValue((Integer) value); } // in BoxOfInt | ||
Bridge methods are not visible in the source code but can be seen in the resulting bytecode. | ||
Have a look disassembling it with javap -c -v BoxOfInt.class | ||
*/ | ||
|
||
} | ||
|
||
public class BridgeMethods { | ||
|
||
public static void main(String... args) { | ||
Box<Integer> boxOfInt = new BoxOfInt(); | ||
boxOfInt.setValue(1); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
public class Container<T extends Number> { | ||
|
||
public static void main(String[] args) { | ||
Container<Integer> ci = new Container<>(); | ||
Container<Double> cd = new Container<>(); | ||
|
||
// Container<String> cs = new Container<>(); // will not compile | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
final class Pineapple extends Fruit {} | ||
final class Melon extends Fruit {} | ||
|
||
public sealed class Fruit<T> permits Pineapple, Melon { | ||
private T t; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
public record GenericRecord<K, V>(K key, V value) { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
class LivingThing { | ||
|
||
} | ||
|
||
class Human extends LivingThing { | ||
private String name; | ||
|
||
public Human(String name) { | ||
this.name = name; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} | ||
|
||
class Student extends Human { | ||
private int fn; | ||
|
||
public Student(String name, int fn) { | ||
super(name); | ||
this.fn = fn; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Student{" + | ||
"fn=" + fn + | ||
'}'; | ||
} | ||
} | ||
|
||
class FMIStudent extends Student { | ||
public FMIStudent(String name, int fn) { | ||
super(name, fn); | ||
} | ||
|
||
} | ||
|
||
public class GenericsPlayground { | ||
|
||
// The Get & Put principle in action | ||
|
||
private static void getHumans(List<? extends Human> listOfHumans) { | ||
|
||
// we can safely get, and we can rely on always getting a Human | ||
for (Human h : listOfHumans) { // we can safely iterate as Human and call methods of Human | ||
System.out.println(h.getName()); | ||
} | ||
|
||
// we cannot add any elements to the list, because the compiler cannot verify the type safety. | ||
// The only exception to this is adding `null`, as `null` is a valid value for any reference type. | ||
listOfHumans.add(null); | ||
|
||
} | ||
|
||
private static void putHumans(List<? super Human> listOfSuperHumans) { | ||
|
||
// we can safely put instances of Human and successors of Human | ||
listOfSuperHumans.add(new Student("Georgi Todorov", 62348)); | ||
listOfSuperHumans.add(new Human("Anelia Angelova")); | ||
listOfSuperHumans.add(new FMIStudent("Zahari Zvezdomirov", 62216)); | ||
// listOfSuperHumans.add(new LivingThing()); // will not compile, why? | ||
|
||
// if we get, we can just rely on getting a java.lang.Object | ||
Object o = listOfSuperHumans.get(0); | ||
if (o instanceof Student) { | ||
System.out.println(((Student) o).getName()); | ||
} | ||
System.out.println(o); | ||
|
||
} | ||
|
||
private static int neitherGetNorPut(List<?> listOfUnknown) { | ||
|
||
// if we get, we can just rely on getting a java.lang.Object | ||
Object o = listOfUnknown.get(0); | ||
|
||
// we can add only `null` | ||
listOfUnknown.add(null); | ||
// listOfUnknown.add("kuku"); // will not compile | ||
|
||
// we can use only methods that are agnostic to the type of elements | ||
return listOfUnknown.size(); | ||
|
||
} | ||
|
||
public static void main(String[] args) { | ||
|
||
List<FMIStudent> listOfFMIStudents = new ArrayList<>(); | ||
List<Student> listOfStudents = new ArrayList<>(); | ||
List<Human> listOfHumans = new ArrayList<>(); | ||
List<LivingThing> listOfLivingThings = new ArrayList<>(); | ||
List<Object> listOfObjects = new ArrayList<>(); | ||
|
||
getHumans(listOfHumans); | ||
getHumans(listOfStudents); | ||
getHumans(listOfFMIStudents); | ||
|
||
putHumans(listOfHumans); | ||
putHumans(listOfLivingThings); | ||
putHumans(listOfObjects); | ||
|
||
System.out.println(neitherGetNorPut(new ArrayList<>(List.of(1, 2, 3)))); // 4 | ||
|
||
} | ||
|
||
} |
86 changes: 86 additions & 0 deletions
86
05-generics/snippets/src/LowerBoundedWildcardGenerics.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
interface Shape { | ||
double area(); | ||
} | ||
|
||
class Rectangle implements Shape, Comparable<Shape> { | ||
private double length; | ||
private double width; | ||
|
||
public Rectangle(double length, double width) { | ||
this.length = length; | ||
this.width = width; | ||
} | ||
|
||
@Override | ||
public double area() { | ||
return length * width; | ||
} | ||
|
||
@Override | ||
public int compareTo(Shape other) { | ||
return Double.compare(this.area(), other.area()); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Rectangle[Area=" + area() + "]"; | ||
} | ||
} | ||
|
||
class Square extends Rectangle { | ||
|
||
public Square(double side) { | ||
super(side, side); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Square[Area=" + area() + "]"; | ||
} | ||
} | ||
|
||
// A utility class that will use lower-bounded wildcard generics | ||
class ShapeUtils { | ||
|
||
// This method leverages generics and bounded wildcards to handle a flexible range of types | ||
// while maintaining type safety. | ||
public static <T extends Shape> T findLargestShape(T[] shapes) { | ||
T largest = shapes[0]; | ||
for (T shape : shapes) { | ||
// The expression `Comparable<? super T>` is used to cast `shape` to ensure compatibility | ||
// with the `compareTo` method. The lower bound (`super`) is necessary for handling the case where `T` | ||
// could be a subtype of `Rectangle`, allowing comparison across derived classes like `Square`. | ||
if (((Comparable<? super T>) shape).compareTo(largest) > 0) { | ||
largest = shape; | ||
} | ||
} | ||
return largest; | ||
} | ||
|
||
// Note that if we change the generic type of the method above to <T extends Shape & Comparable<T>>, | ||
// the `shapes` array can only contain elements of the same type `T`, because `Comparable<T>` restricts `T` | ||
// to be compared only with objects of the same type. | ||
// As a result, it would not be possible to use this method with an array of mixed types like | ||
// `[Rectangle, Rectangle, Square, Square]`, reducing its flexibility. | ||
} | ||
|
||
// Main class to test the implementation | ||
public class LowerBoundedWildcardGenerics { | ||
public static void main(String[] args) { | ||
Rectangle rect1 = new Rectangle(4, 5); | ||
Rectangle rect2 = new Rectangle(2, 3); | ||
Square square1 = new Square(3); | ||
Square square2 = new Square(4); | ||
|
||
Shape[] shapes = {rect1, rect2, square1, square2}; | ||
Rectangle[] rectangles = {rect1, rect2, square1, square2}; | ||
Square[] squares = {square1, square2}; | ||
|
||
// Find the largest shape | ||
Shape largestShape = ShapeUtils.findLargestShape(shapes); | ||
//Shape largestRectangle = ShapeUtils.findLargestShape(rectangles); | ||
//Shape largestSquare = ShapeUtils.findLargestShape(squares); | ||
|
||
System.out.println("The largest shape is: " + largestShape); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class Pair<K, V> { | ||
|
||
private K key; | ||
private V value; | ||
|
||
public Pair(K key, V value) { | ||
this.key = key; | ||
this.value = value; | ||
} | ||
|
||
public K getKey() { | ||
return key; | ||
} | ||
|
||
public void setKey(K key) { | ||
this.key = key; | ||
} | ||
|
||
public V getValue() { | ||
return value; | ||
} | ||
|
||
public void setValue(V value) { | ||
this.value = value; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Pair{" + | ||
"key=" + key + | ||
", value=" + value + | ||
'}'; | ||
} | ||
|
||
public static void main(String[] args) { | ||
Pair<String, Integer> pair1 = new Pair<>("Stoyo", 1); | ||
Pair<String, Integer> pair2 = new Pair<>("Boyo", 6); | ||
|
||
Pair<Double, Double> pair3 = new Pair<>(1.0, 1.0); | ||
|
||
System.out.println(Util.areEqual(pair1, pair2)); | ||
// the next line will not compile: "reason: Incompatible equality constraint: Double and String" | ||
//System.out.println(Util.areEqual(pair1, pair3)); | ||
|
||
Pair<List<String>, Set<Integer>> pair4 = new Pair<>(List.of("FMI", "rulez"), Set.of(2024, 2025)); | ||
} | ||
|
||
} | ||
|
||
class Util { | ||
// Generic static method | ||
public static <K, V> boolean areEqual(Pair<K, V> p1, Pair<K, V> p2) { | ||
return p1.getKey().equals(p2.getKey()) && | ||
p1.getValue().equals(p2.getValue()); | ||
} | ||
} |