0% found this document useful (0 votes)
41 views47 pages

JAVA Development: Boxing, Generics, Comparable, Iterable

This document provides a 3 sentence summary of the Java document: The document discusses Java concepts including boxing, generics, and comparable and iterable interfaces. It explains that boxing allows primitive values to be wrapped in reference types and automatically handled by the compiler. Generics allow types or methods to operate on different types while providing compile-time safety. Bounded types allow restricting the types that can be passed to type parameters.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
41 views47 pages

JAVA Development: Boxing, Generics, Comparable, Iterable

This document provides a 3 sentence summary of the Java document: The document discusses Java concepts including boxing, generics, and comparable and iterable interfaces. It explains that boxing allows primitive values to be wrapped in reference types and automatically handled by the compiler. Generics allow types or methods to operate on different types while providing compile-time safety. Bounded types allow restricting the types that can be passed to type parameters.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 47

JAVA Development

Boxing, Generics,
Comparable, Iterable
Boxing
Objects vs Primitives

In Java, (almost) everything is-an Object…

- java.lang.Object is the root class that all classes inherit

print("a" instanceof Object); // -> true


print((new Dog()) instanceof Object); // -> true

Object dog = new Dog(); // OK! Dog is an Object


Object str = "some string"; // OK! String is an Object

... except primitives!

Only reference types are objects.


Boxing and Unboxing

Boxing = wrapping a primitive value in a reference type

- Each primitive type has a corresponding wrapper type:


- boolean, byte, short, char, int, long, float, double =>
- Boolean, Byte, Short, Character, Integer, Long, Float, Double

- Conversions ( boxing / unboxing ) are handled automatically by the compiler:


Integer intBoxed = 4; //boxing: int ->Integer
Long longBoxed = 34L; //boxing: long->Long
long primitive = longBoxed + 1L; //unboxing: Long->long

- You may explicitly cast to a type if/when needed:


double b = (double) longBoxed; //explicit unboxing+casting: Long->double
Boxing - Advantages

- You can work with primitives uniformly, as if they were objects.

static void print(Object o) {


System.out.println(o.getClass().getName() + ": " + o.toString());
}

print(3); //-> java.lang.Integer: 3
print(true); //-> java.lang.Boolean: true
print(new Dog("Azor")); //-> com.wantsome.week4.Dog: Azor

//assuming that we’ve overridden toString() in the Dog class:


class Dog {
String name;
public String toString() { return this.name; }
}
Boxing - Drawbacks

- You have to deal with null values - as primitives cannot be null, but references can:

//failure on automatic unboxing (Long -> long):


Long l1 = null; //ok for wrapper, but can’t convert it to primitive anymore
Long l2 = l1 + 1; // <- Exception in thread "main"
java.lang.NullPointerException

//failure on another automatic unboxing (Boolean -> boolean):


Boolean b = null;
if (b) {…} // <- NullPointerException again!

- They have worse performance


● An Integer[] is no longer just contiguous memory
● Adds memory overhead for every single value (compared to a primitive)
Boxing - Usage

Recommendation: if possible, prefer working with primitives!

- Especially for code that needs to be high performance

- 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:

- type variable = placeholder for a generic/unspecified type

- generic method/constructor = a method/constructor which declares one or more


type variables

- generic class/interface = a class/interface which declares one or more type


variables, on class level (a kind of class parameters, but for types, not values)
Type parameters - Naming conventions

- By convention, type parameter names should be single, uppercase letters.


This is different from naming conventions for regular classes/interfaces, and the purpose is to
make it easy to differentiate in code between a type variable and an ordinary class/interface.

- The most commonly used type parameter names are:


- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
- R - Return type (of a method)
- S, U, V etc. - 2nd, 3rd, 4th types
Generic Methods

public static <T,U,...> ReturnType methodName(ParamType1 param1, …) {…}

• 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

//generic method: public static void main(String args[]) {


static <E> void printArray(E[] arr){
//create arrays of different types:
for (E elem : arr) { Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
System.out.prinln(elem + " ");
Character[] charArray= {'H','E','L','L','O'};
}
System.out.println(); //print them using same generic method:
} System.out.println("intArray contains:");
printArray(intArray); //pass an Integer array

System.out.println("doubleArray contains:");
printArray(doubleArray); //pass a Double array

System.out.println("charArray contains:");
printArray(charArray); //pass a Character array
}
Generic Classes

class ClassName <T,U,..> {...}

• 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

class Box<T> { class BoxDemo {


public static void main(String[] args) {
private T t;
Box<Integer> intBox = new Box<Integer>();
Box<String> strBox = new Box<String>();
public void add(T t) {
this.t = t; intBox.add(new Integer(10));
} strBox.add("Hello World");

public T get() { System.out.println("Integer: " + intBox.get());


return t; System.out.println("String: " + strBox.get());
} }
} }
Generic Classes - Subtyping

• We can subtype a generic class/interface by extending/implementing it. The relationship


between the type parameters of one class/interface and the type parameters of another are
determined by the extends / implements clauses.

• the subtyping relationship is preserved as long as we don’t change the type argument

Example:

interface MyList<E, T> extends List<E> {}

-> the subtypes of List<String> can be:


MyList<String,Object>, MyList<String,Integer> and so on…
Generics - Bounded types

• 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 {

//determines largest of three public static void main(String args[]) {


static <T extends Comparable<T>> T max (T x, System.out.printf("Max (%d, %d, %d): %d\n",
T y, 3, 4, 5, max(3, 4, 5));
T z) {
T m = x.compareTo(y) > 0 ? x : y; System.out.printf("Max (%.1f,%.1f,%.1f): %.1f\n",
6.6, 8.8, 7.7, max(6.6, 8.8, 7.7));
m = m.compareTo(z) > 0 ? m : z;
System.out.printf("Max of (%s, %s, %s): %s\n",
return m; "pear", "apple", "orange",
} max("pear", "apple", "orange"));
}

}
Generics – Wildcards

• the “?” (question mark) symbol represents wildcard element


• it means any type (for which we don’t need to set a name for some other use)
• has some restrictions for use (not supported in all places like named type params)

Example: <? extends Number> - means any child class of Number (like: Integer, Float..)
import java.util.ArrayList;
import java.util.List;

public class GenericsWildcards {

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>

public static void printData(List<?> list) {


for(Object obj : list) {
System.out.print(obj.toString() + "::");
}
}
Generics – Lower bounded wildcards

import java.util.ArrayList;
import java.util.List;

public class LowerBoundedWildcard {

static void addIntegers(List<? super Integer> list) {


list.add(new Integer(50));
}

public static void main(String[] args) {


List<Number> myList = new ArrayList<Number>();
addIntegers(myList); //Number is a super-type of Integer, so List<Number> is accepted here...
System.out.println("myList=" + myList);
}
}
Generics – Benefits

• type-safety: we can hold only a single type of objects in generics


• type casting not required: we do not need to typecast the object
• compile time checking: type errors detected at compile time, avoiding issues at runtime
Without generics:

List list = new ArrayList(); //can hold any Object!


list.add("hello");
String s = (String) list.get(0); //casting required here
list.add(1); //is allowed! (adds an Integer)

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>();

//...we can write this:


List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
Box<Integer> intBox = new Box<>();
Generics – Class example

/**
* 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}
}

public T getPart1() { return part1; }


public U getPart2() { return part2; }

@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!

source code: compile code:

<E> boolean contains(E[] elements, boolean contains(Object[] elements,


E element) { Object element) {
for (E e : elements) { for (Object e : elements) {
if (e.equals(element)) { if (e.equals(element)) {
return true; return true;
} }
} }
return false; return false;
} }
Type Erasure

source code: after compile:


class MaxTest { class MaxTest {

//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) {

T m = x.compareTo(y) > 0 ? x : y; Comparable m = x.compareTo(y) > 0 ? x : y;

m = m.compareTo(z) > 0 ? m : z; m = m.compareTo(z) > 0 ? m : z;

return m; return m;
} }
} }
Type Erasure – special case

class IntegerStack extends Stack<Integer> {


class Stack<E> { IntegerStack(int capacity) {
private E[] content; super(capacity);
}
private int top = 0;
void push(Integer value) {
Stack(int capacity) { super.push(value);
content = (E[]) new Object[capacity]; }
} }

void push(E data) { public class StackTest {


content[top++] = data; public static void main(String args[]) {
} IntegerStack integerStack = new IntegerStack(5);

Stack stack = integerStack; //raw (unchecked) access!!


E pop() {
return content[top--]; stack.push("Hello"); //=> !! throws ClassCastException:
} //java.lang.String cannot be cast to java.lang.Integer
}
Integer data = integerStack.pop();
}
}
Comparable,
Comparator
Comparable

public interface Comparable<T> {


public int compareTo(T that);
}

java.lang.Comparable - implemented by classes which need to support direct comparison of


their instances, like for sorting (they have a default ordering which we generally use)

Meaning of compareTo() return value:


-1 => this < that
0 => this == that
1 => this > that

Note: not everything is comparable! For example, it probably doesn’t make sense to compare
two Point instances from the previous slides.
Comparable - Definition

Implementing the Comparable interface:

If we want to order Products by their price:


class Product implements Comparable<Product> {
double price;

public int compareTo(Product o) {


if (this.price < o.price) { return -1; }
else if (this.price == o.price) { return 0; }
else { return 1; } //this.price > o.price (only case left)
}
}
Comparable - Definition

Notes:
- In many cases you can just delegate to other .compareTo() calls (on some other
fields/variables of a Comparable type)

class Product implements Comparable<Product> {


Double price; //note the use of Double here (instead of double)
public int compareTo(Product o) {
return price.compareTo(o.price); //as Double is Comparable..
}
}

- 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

Using objects implementing Comparable:

List<Product> products = Arrays.asList(


new Product("phone", 3000d),
new Product("55 inch LG tv", 5000d),
new Product("tablet", 2000d),
new Product("laptop", 8000d));

List<Product> list = new ArrayList<>(products);

Collections.sort(list); //uses Product.compareTo(), so sort them by price

System.out.println(list);
//-> [tablet:2000.0, phone:3000.0, 55 inch LG tv:5000.0, laptop:8000.0]
Comparator

public interface Comparator<T> {


int compare(T o1, T o2);
}

- java.util.Comparator - implemented when you need a custom/extra ordering for a class


(besides the one it already has due to being Comparable), or you don’t want to modify the class.
- compare() has same meaning like compareTo() ( result: -1: o1<o2, 0: o1==o2, 1: o1>o2)

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> {

public int compare(Product p1, Product p2) {


return p1.getName().compareTo(p2.getName());
}
}

- Similar notes to .compareTo():


- be careful regarding comparing null values!
- make sure this rule is respected: compare(a, b) == - compareTo(b, a)
Comparator - Usage

Using a Comparator:

List<Product> products = Arrays.asList(


new Product("phone", 3000d),
new Product("55 inch LG tv", 5000d),
new Product("tablet", 2000d),
new Product("laptop", 8000d));

List<Product> list = new ArrayList<>(products);

Collections.sort(list, new ProductNameComparator());

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):

List<Product> products = Arrays.asList(


new Product("phone", 3000d),
new Product("55 inch LG tv", 5000d),
new Product("tablet", 2000d),
new Product("laptop", 8000d));

Set<Product> setByPrice = new TreeSet<>(); //with default sorting (from Comparable)


setByPrice.addAll(products);
System.out.println(setByPrice); //-> [tablet:2000, phone:3000, 55 inch LG tv:5000, laptop:8000]

Set<Product> setByName = new TreeSet<>(new ProductNameComparator()); //with custom sorting!


setByName.addAll(products);
System.out.println(setByName); //-> [55 inch LG tv:5000, laptop:8000, phone:3000, tablet:2000]
Iterable,
Iterator
Iterable & Iterator

Strong relationship between Iterator and Iterable:

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();
}

- hasNext(): returns true if more elements are available


- next(): produces a new element
- remove(): if supported, removes the current element from the underlying collection
- Note: this is the only safe way to remove elements of a collection while still
iterating over it! (without getting a ConcurrentModificationException)
Iterable

interface Iterable<E> {
Iterator<E> iterator();
}

Note: the for ( : ) iteration syntax works for all Iterables! (not just arrays, Collections..)
Iterator, Iterable - Example

Let’s say we want to iterate over all powers of a number:

class PowersOf implements Iterable<Long> {


private long number;

public PowersOf(long n) {
this.number = n;
}

public Iterator<Long> iterator() {


return new PowersOfIterator(number);
}
}
Iterator, Iterable - Example

And the iterator that actually generates the powers:

public class PowersOfIterator implements Iterator<Long> {


private long number, currentValue = 1;

public PowersOfIterator(long number) { this.number = number; }

public boolean hasNext() { return true; } //goes on forever..

public Long next() {


currentValue *= number; //go to the next power
return currentValue; //return it
}

public void remove() { /*not supported*/ }


}
Iterator, Iterable - Example

And to iterate through the powers of 3:

Iterable<Long> powersOf3 = new PowersOf(3);

for (long p : powersOf3) {


System.out.println(p);
if (p > 1000000) {
break;
}
}
//-> prints 3, 9, 27, 81, 243, 729 .... 1594323

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!!
}
}

System.out.println("after remove: " + list);


Questions?
Extra reading

- 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

You might also like