JAVA Development: Boxing, Generics, Comparable, Iterable
JAVA Development: Boxing, Generics, Comparable, Iterable
Boxing, Generics,
Comparable, Iterable
Boxing
Objects vs Primitives
- You have to deal with null values - as primitives cannot be null, but references can:
- Also when you want to avoid supporting/handling the case of null value
- including the return type of functions, class fields ..
- ask yourself: do I ever need/expect this to be null? or do I have any other good
reason to make this a wrapper type, and not a primitive?...
Generics
Generics
- Since Java 5
- an extension of Java type system to allow “a type or method to operate on
objects of various types while providing compile-type safety”
- in Java libs: used mainly in collections framework, but also some other places
Example:
//without generics: //with generics:
List v = new ArrayList(); //holds any Object List<String> v = new ArrayList<String>();
v.add("test"); v.add("test");
//compiles, but throws runtime error later String s = v.get(0); //ok (without cast)
Integer i = (Integer) v.get(0); //cast needed
//(type error) => compile-time error!
Integer i = v.get(0);
Generics
Concepts:
• a generic method declaration has a type parameter section delimited by angle brackets
(<...>) and placed before the method's return type
○ the type parameter section ( < T, U,... > here) contains one or more type parameters,
separated by commas
• a type parameter (‘type variable’) is an identifier that specifies a generic type name
○ note: type parameters can represent only reference types! (not primitives)
• the type parameters can then be used like any other fixed type, in that method’s declaration
and implementation (like for: types of input parameters, return type, local variables..)
○ except: you cannot create new instances of them (with new()..)
Generic Methods - Example
System.out.println("doubleArray contains:");
printArray(doubleArray); //pass a Double array
System.out.println("charArray contains:");
printArray(charArray); //pass a Character array
}
Generic Classes
• a generic class (/interface) declaration looks like a non-generic declaration, except that the
class (/interface) name is followed by a type parameter section. (<T,U,..> here)
• as with generic methods, the type parameter section of a generic class can have one or
more type parameters separated by commas.
• these classes are known as parameterized classes or parameterized types because they
accept one or more parameters
• the type parameters can then be used in the body of the class/interface like any other fixed
type (for class fields, methods parameters and return type, local variables..)
Generic Classes - Example
• the subtyping relationship is preserved as long as we don’t change the type argument
Example:
• when you want to restrict the types that are allowed to be passed to a type parameter.
• 2 types:
○ upper bounded - accept a type T and all its sub-types (T is the upper limit)
○ lower bounded - accept a type T and all its super-types (T is the lower limit)
• declaring an upper bounded type parameter: after the type parameter name add the extends
keyword, followed by its upper bound: <T extends UpperBoundType>
• declaring a lower bounded type parameter: after the type parameter name add the super
keyword, followed by its lower bound: <T super LowerBoundType>
Examples:
<T extends Number> - for T we can pass only Number or one of its subtypes (like: Integer,Double..)
<T super Integer> - for T we can pass only Integer or one of its supertypes (like: Number..)
Generics - Upper bounded types example
class MaxDemo {
}
Generics – Wildcards
Example: <? extends Number> - means any child class of Number (like: Integer, Float..)
import java.util.ArrayList;
import java.util.List;
static double sum(List<? extends Number> nums){ public static void main(String[] args){
double sum = 0; List<Integer> ints =
for (Number n : nums) { Arrays.asList(12, 2, 10);
sum += n.doubleValue();
} double s = sum(ints);
return sum; System.out.println("Sum: " + s);
} }
}
Generics – Wildcards
• if we have a situation where we want our generic method to be working with all types, we
can use just <?>. It’s same as using <? extends Object>
import java.util.ArrayList;
import java.util.List;
With generics:
List<String> list = new ArrayList<String>(); //can hold only Strings!
list.add("hello");
String s = list.get(0); //casting not needed (value is already String)
list.add(1); //not allowed -> compile error!
Generics – Diamond operator
- Since Java7: we can use a pair of empty brackets - ‘<>’, called the diamond operator -
instead of a pair of brackets with one or more type parameters, in places where the
compiler can automatically deduce the expected types => we reduce code duplication
Examples:
//instead of this...
List<String> list = new ArrayList<String>();
Map<String, Integer> map = new HashMap<String, Integer>();
Box<Integer> intBox = new Box<Integer>();
/**
* Generic pair of 2 values of different types
* @param <T> type of 1st part
* @param <U> type of 2nd part
//…
*/
//using Pair class;
class Pair<T, U> {
Pair<String, Integer> strIntPair = new Pair<>("abc", 1);
private final T part1;
Pair<Integer, String> intStrPair = new Pair<>(2, "xyz");
private final U part2;
//strIntPair = intStrPair; //compile err, diff types!
Pair(T part1, U part2) {
Pair<Integer, Double> pair = new Pair<>(1, 2.3);
this.part1 = part1;
System.out.println("pair: " + pair);
this.part2 = part2;
//=> pair: Pair{part1=1, part2=2.3}
}
@Override
public String toString() {
return "Pair{" + "part1=" + part1 +
", part2=" + part2 +'}';
}
}
Type Erasure
Type Erasure
• type erasure = the process of enforcing type constraints only at compile time and discarding
the element type information at runtime
○ this is done for performance reasons (is different in other languages, like C#)
• the compiler ensures type safety of our code, preventing later runtime errors
○ type safety - works as long as we avoid raw (unchecked) access in our code!
//returns the largest of three Comparables //returns the largest of three Comparables
static <T extends Comparable<T>> T max(T x, static Comparable max(Comparable x,
T y, Comparable y,
T z){ Comparable z) {
return m; return m;
} }
} }
Type Erasure – special case
Note: not everything is comparable! For example, it probably doesn’t make sense to compare
two Point instances from the previous slides.
Comparable - Definition
Notes:
- In many cases you can just delegate to other .compareTo() calls (on some other
fields/variables of a Comparable type)
- be careful about comparing null values! (in above example, exception if this.price is null)
- make sure your implementation respects the rule: a.compareTo(b) == - b.compareTo(a)
Comparable - Usage
System.out.println(list);
//-> [tablet:2000.0, phone:3000.0, 55 inch LG tv:5000.0, laptop:8000.0]
Comparator
Comparator vs Comparable:
- a class T can implement Comparable<T> only once (and will encapsulate that specific
ordering logic in the class itself) -> good if you have a general/default ordering...
- we can define multiple comparator classes, all implementing Comparator<T>, which can
then be used to compare instances of class T by various criteria (the ordering logic will be
external to the class T for this case)
Comparator - Definition
Defining a Comparator:
- Let’s say we want to compare products by their name, not their price:
class ProductNameComparator implements Comparator<Product> {
Using a Comparator:
System.out.println(list);
//-> [55 inch LG tv:5000.0, laptop:8000.0, phone:3000.0, tablet:2000.0]
Comparator - Usage for sorted collections
Comparators can also be passed as constructor params to some sorted collections (TreeSet,
TreeMap), to keep them sorted in a specific way (instead of the order from Comparable):
java.util.Iterator:
● an object that can produce elements, or iterate over elements
● generally produced by an Iterable and not instantiated directly
java.lang.Iterable:
● an object that can produce an Iterator over its own elements (e.g a List, a Set)
Iterator
interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
interface Iterable<E> {
Iterator<E> iterator();
}
Note: the for ( : ) iteration syntax works for all Iterables! (not just arrays, Collections..)
Iterator, Iterable - Example
public PowersOf(long n) {
this.number = n;
}
Note: this iterator can go on forever! The for loop stops only due to that break statement.
Iterator - Remove
Iterator.remove() = the (only) safe way to remove elements while still iterating over a collection!
List<Integer> list = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5)); //copy constructor
//1) First attempt: removing elements while still iterating over collection -> NOT safe, throws exception!
for (Integer i : list) {
if (i < 0) {
list.remove(i); //=> throws: java.util.ConcurrentModificationException
}
}
//2) 2nd attempt: it's safe to do with Iterator! (but only by calling .remove() from iterator, not list)
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer i = it.next();
if (i < 0) {
//list.remove(i); //this still throws exception! (calling remove directly on the list)
it.remove(); //but this works ok and is safe!!
}
}
- https://www.baeldung.com/java-wrapper-classes
- https://www.baeldung.com/java-generics
- https://www.baeldung.com/java-comparator-comparable
- https://www.baeldung.com/java-iterator
- https://www.baeldung.com/java-hashcode
- https://en.wikipedia.org/wiki/Generics_in_Java