advanced java programming - unideb.hu · advanced java programming •java 5 •generics •(enums)...

41
Advanced Java Programming Programming Technologies 2015/2016 spring Kollár, Lajos Kocsis, Gergely (English version)

Upload: others

Post on 21-May-2020

22 views

Category:

Documents


0 download

TRANSCRIPT

Advanced Java ProgrammingProgramming Technologies

2015/2016 spring

Kollár, Lajos

Kocsis, Gergely (English version)

Advanced Java Programming

• Java 5• Generics• (Enums)

• Java 7• Strings in switch• try-with-resources

• Java 8• Default methods of interfaces• Lambda-expressions• Functional interfaces• Streams• Others

Generics in JavaSee more:

http://javarevisited.blogspot.sg/2011/09/generics-java-example-tutorial.html

http://docs.oracle.com/javase/tutorial/java/generics/

http://thegreyblog.blogspot.hu/2011/03/java-generics-tutorial-part-i-basics.html

Generics• Goal: type safety during runtime (preventing type errors)

• To avoid runtime ClassCastException

• Type erasure: • Type information is only available in compilation time. It’s not present in the byte-code

• This implies that it cannot be reached in runtime

• E.g.: In case of List<String> in the byte-code only the List raw type is present

• Even casting may be compiled into the code if needed

• Type inference, since Java 7):• Based on the method calls and declarations the Java compiler can find out those type

parameters that make the call possible

• It finds the most specific such type

Notations, naming conventions Generics concept Meaning

Set<E> Generic type, E is the formal type parameter

Set<Integer> Parameterized type, Integer the actual type parameter

<T extends Comparable> Bounded type parameter

<T super Comparable> Bounded type parameter

Set<?> Unbounded wildcard

<? extends T> Bounded wildcard type

<? super T> Bounded wildcard type

Set Raw type

<T extends Comparable<T>> Recursive type bound

T – typeE – elementK – keyV - valueN – number

Wildcards

• Bounded wildcard: • <? extends T>

• Wildcards are covariant to their upper bound

• <? super T>• Wildcards are contravariant to their lower bound

• Unbounded wildcard: • <?>

• Equivalent with <? extends Object

• The Set<T> parameterized type is the subtype of the Set raw type:

Set rawTypeSet = new HashSet<String>();

rawTypeSet = new HashSet<Integer>();

•Set<Object> can hold any element:Set<Object> anyTypeSet = new HashSet<Object>();

anyTypeSet.add("abc"); //legal

anyTypeSet.add(new Float(3.0f)); //legal

anyTypeSet = new HashSet<Integer>(); //illegal

• Set<?> marks unknown type, legal options are:Set<?> unknownTypeSet = new LinkedHashSet<String>();

unknownTypeSet = new LinkedHashSet<Integer>();

• Parameterized types follow inheritance on the level of types:

Set<String> setOfString = new HashSet<String>(); //legal

setOfString = new LinkedHashSet<String>(); //legal

But this is not true for type parameters! E.g. We cannot instantiate Set<Object> as a Set<String>.

• Set<? extends Number> can contain any number element:

Set<? extends Number> anyNumberTypeSet = new HashSet<Integer>(); //legal, because Integer extends Number

anyNumberTypeSet = new HashSet<Float>(); //also legal

• We cannot use generics in class literals

List.class //legal

List<Integer>.class //compilation error

• Set<? super TreeMap> can hold TreeMap objects, or any super of it:

Set<? super TreeMap> treeMapSuperTypeSet = new

LinkedHashSet<TreeMap>(); //leglegallis

treeMapSuperTypeSet = new HashSet<SortedMap>(); //legal

since SortedMap is the superclass of TreeMap

treeMapSuperTypeSet = new LinkedHashSet<Map>(); //legal

since Map is the superclass of TreeMap

• When writing generic methods the place in the signature of the type parameter is between the modifiers and the return type e.g:

public static <T> T identical(T source){

return source;

}

Definitions

In the type system of a programming language a rule or type constructor

• covariant, if it keeps the ordering of types from specific to generic;

• contravariant, if it reverses the order;

• invariant, if none of the above is true.

• Source: Andrey Tyukinhttp://stackoverflow.com/a/19739576

Covariance• In Java arrays are covariant

• Elements of T[] can be of type T, or any subtypes of T

Number[] numbers = new Number[3];

numbers[0] = new Integer(10);

numbers[1] = new Double(3.14);

numbers[2] = new Byte(0);

• S[] is a subtype of T[], if S is also a subtype of T. So the following is legal:

Integer[] myInts = {1,2,3,4};

Number[] myNumber = myInts;

• What happens?

myNumber[0] = 3.14; //ArrayStoreException unchecked exception

CovarianceList<? extends Number> covariantList = new ArrayList<Integer>();

• List of such elements, that are of type Number or any subtype of it

• Can be read• Because anything in the list can be casted to Number

Number n = covariantList.get(0); //legal

• This throws however a runtimeException:

Integer i = covariantList.get(0); //RuntimeException

• We cannot write• Because the compiler does not know the exact type in the structure

• This can be any subtype of the Number type – e.g. Integer, Double, LongcovariantList.add(45L); //compilation error

covariantList.add(123); //compilation error

ContravarianceList<? super Number> contravariantList = new ArrayList<Object>();

• List of such elements, that are of type Number or any supertype of it

• We can write• We can put anything to the more generic typecontravariantList.add(100); //legal

contravariantList.add(43L); //legal

contravariantList.add(3.14); //legal

contravariantList.add(o); //compilation error

• But we cannot read anything!• Except Object!Integer i = contravariantList.get(0); //compilation error

Number n = contravariantList.get(0); //compilation error

Object o = contravariantList.get(0); //legal

Type inference „cube”

"Java wildcard subtyping" by Vilhelm.s - Own work. Licensed under CC BY-SA 3.0 via Wikimedia Commons -http://commons.wikimedia.org/wiki/File:Java_wildcard_subtyping.svg#mediaviewer/File:Java_wildcard_subtyping.svg

PECS: Producer extends, consumer super

• get and put principle

• Use extends, if we only get from the structure

• Use super if we only put to the structure

• Do not use any if we both read and write

• Example: In case of T type elements Collection (Collection<T>)• If we go through the Collections elements in order to do something with them

• The collection is the producer -> use Collection<? extends T>-t, so that we can get any subtype of T as well

• If we would like to put something to the Collection• The collection is the consumer -> use Collection<? super T>, since it does not

matter what is in the collection while we can add T type elements

Example use of extends

public static double sum(Collection<? extends Number> nums) {

double s = 0.0;

for (Number num : nums)

s += num.doubleValue();

return s;

}

List<Integer> ints = Arrays.asList(1,2,3); //sum(ints) == 6.0;

List<Double> doubles = Arrays.asList(2.78,3.14); //sum(doubles) == 5.92;

List<Number> nums = Arrays.<Number>asList(1,2,2.78,3.14); //sum(nums) == 8.92;

Example use if super

public static void count(Collection<? super Integer> ints, int n) {

for (int i = 0; i < n; i++)

ints.add(i);

}

List<Integer> ints = new ArrayList<Integer>();

count(ints, 5); //[0, 1, 2, 3, 4]

List<Number> nums = new ArrayList<Number>();

count(nums, 5); //[0, 1, 2, 3, 4]

nums.add(5.0); //[0, 1, 2, 3, 4, 5.0]

List<Object> objs = new ArrayList<Object>();

count(objs, 5); //[0, 1, 2, 3, 4]

objs.add("five"); //[0, 1, 2, 3, 4, "five"]

When not to use any of the two?

public static double sumCount(Collection<Number> nums, int n) {

count(nums, n);

return sum(nums);

}

Use of generics – best practices

• Try to avoid raw types.• Use generic types, parameterized classes and methods.

• Prefer collections instead of arrays.

• Use bounded type parameters!• So that we can produce flexible API

• Use @SuppressWarnings("unchecked") only if it is really needed• E.g. instead of using it on a method use it only on a given row

• Convert the raw types of our old classes to generics!• Our code get more robust

String in switchSee more:

http://www.theserverside.com/tutorial/The-Switch-to-Java-7-Whats-New-with-Conditional-Switches

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

public static String getDayType(String dayOfWeek) {String dayType;if (dayOfWeek == null)

throw new IllegalArgumentException("No such day: " + dayOfWeek);switch (dayOfWeek) {

case „monday":dayType = "Start of the week";break;

case „Tuesday":case „Wednesday":case „Thursday":

dayType = "Middle of the week";break;

case „Friday":dayType = "End of the week";break;

case „Saturday":case „Sunday":

dayType = "Weekend";break;

default:throw new IllegalArgumentException ("No such day: " + dayOfWeek);

}return dayType;

}

Strings in switch

• More easy to read that nested if-else statements• The generated byte code is also more efficient

• Strings are case-sensitive

• Strings are compared by the equals method• Avoid NullPointerException

• Case labels are constant expressions

• The static type of the selector has to be String• It is not enough if it is dynamically a String

try–with–resourcesSee more:

http://tutorials.jenkov.com/java-exception-handling/try-with-resources.html

http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

Resource handling before…

private static void printFile() throws IOException {InputStream input = null;

try {input = new FileInputStream("file.txt");int data = input.read();while (data != -1) {

System.out.print((char) data);data = input.read();

}} finally {if (input != null)input.close();

}}

Where can an exception be thrown?

This has to go to another try

…and now (since Java 7)

private static void printFileJava7() throws IOException {

try (FileInputStream input = new FileInputStream("file.txt")) {int data = input.read();while (data != -1) {

System.out.print((char) data);data = input.read();

}}

}

try–with–resources

• Makes sure that the used resources in the statement are closed automatically

• The resource has to implement the java.lang.AutoCloseable interface

• More resources can be used:

private static void printFileJava7() throws IOException {try (FileInputStream input = new FileInputStream("file.txt");

BufferedInputStream bufferedInput = new BufferedInputStream(input)) {int data = bufferedInput.read();while (data != -1) {

System.out.print((char) data);data = bufferedInput.read();

}}

}

Default methods in interfaces

Problems with interfaces

• Evolution of already existing interfaces• What happens if we need to add a new method to an interface?

• E.g.. Add stream(), parallelStream() and. forEach() methods to the collection interfaces

• We have to implement the new method in all the implementing classes• If we do not do the classes will not even compile

• Especially problematic in case of 3rd party applications

Problems with interfaces

• Not enough flexible design• We can distribute functionalities between classes through abstract

classes• Only one superclass is allowed this restricts the use

• In many cases so called adapter classes are needed in order not to have to implement all the methods in all the classes e.g:• SAX-fprocessing: ContentHandler etc. interfaces vs. DefaultHandler

• Swing: MouseListener etc. interfaces vs. MouseAdapter class

• These classes are of good technical use but make the code much harder to read, and also hide which exact interfaces are implemented

Solution: default methods

• Alternative names: defender methods, virtual extension methods

• Default methods cannot have methods with signatures matching to non final methods of the Object class• Implies runtime exception

• Default methods are given in the interfaces, but can be overridden in the implementing classes• No problem in the implementing classes in case of evolution• No need to use adapter classes

• Compatible with classes depending on old interfaces

• Required tools:• default keyword• A block containing the default implementation

• Drawback: implementation appears in the interface

Example

public interface Addressable {String getStreet();

String getCity();

default String getFullAddress() {return getStreet() + ", " + getCity();

}}

public class Letter implements Addressable {private String street, city;

public Letter(String street, String city) {this.street = street; this.city = city;

}

@Overridepublic String getCity() {

return city;}

@Overridepublic String getStreet() {

return street;}

@Overridepublic String getFullAddress() {

return city + ", " + street;}

public static void main(String[] args) {Letter l = new Letter("123 AnyStreet", "AnyCity");System.out.println(l.getFullAddress());

}}

123 AnyStreet, AnyCity

Possible options when inheriting default methods from interfaces• Do not mention the method

• The subinterface inherits the method as it is

• Redeclare the method• This makes it abstract

• Redefine the• We give a new body to it.

Lambda-expressionsSee more:

http://javarevisited.blogspot.hu/2014/02/10-example-of-lambda-expressions-in-java8.html

http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

http://www.slideshare.net/langer4711/lambdas-funct-progjdays2013

http://www.slideshare.net/martyhall/lambda-expressions-and-streams-in-java-8-presented-by-marty-hall-to-google-inc

http://www.drdobbs.com/jvm/lambda-expressions-in-java-8/240166764

Antecedents – Nested class

• Static nested class

• Inner class• Local class

• Anonymous class

See more: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

Nested class

• A class declared inside another• All four visibilities are can be used(public, protected, package private, private)

• Ordinary classes can only be public or private

• a member of the enclosing class

• Static• Terminology: static nested class

• Cannot reach the members of the enclosing class

• Non static• Terminology: inner class

• Can reach the members of the enclosing class even if they are private

Advantage of nested classes

• We can group logically our classes that we use only in one place• If a class is useful for only one other class it is natural that it it better to store

them together.

• Increases closure• If classes A and B are both top-level classes B cannot reach private members of

A. However if we nest B to A, B can reach these members and moreover B gets also hidden.

• The code gets easier to be read and maintain

Lambda-expressions

• We can refer to an instance of a class implementing a functional interface.

new SomeInterface(){

@Override

public SomeType someMethod(parameters) {

body

}

}

Instead we can use:

(parameters) -> {body}

{

return p.getGender() == Person.Sex.MALE

&& p.getAge() >= 18

&& p.getAge() <= 25;

}

Lambda-expressions

• Formal parameters between parentheses separated by commas• The type of the parameter is optional• If there is only one parameter the () are also optional• If there are no parameters however () is mandatory

• -> (arrow) token

• A body holding only one expression or block• If it is an expression it gets evaluated• return can also be used

• Since this is not an expression we put it into a block

• void methods do not have to be put to a block:email -> System.out.println(email)

p ->

Lambda expressions with multiple formal parameterspublic class Calculator {

interface IntegerMath {

int operation(int a, int b);

}

public int operateBinary(int a, int b, IntegerMath op) {

return op.operation(a, b);

}

public static void main(String... args) {

Calculator myApp = new Calculator();

IntegerMath addition = (a, b) -> a + b;

IntegerMath subtraction = (a, b) -> a - b;

System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition));

System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction));

}

}

Visibility

• From the point of visibility lambda expressions do not introduce a new level („lexically scoped”)• No hole in the scope

• Declarations inside are handled as if they were in the enclosing environment

• They cannot change the value of a local variable• final or effective final