Advanced Programming
SUT • Spring 2019
Enumerations
Static Import
Annotation
Suppose you have a class with a few instances
Example:
Student Type : <BS, MS, PhD>
SMS Status : <Sent, Delivered, Not Delivered, Not Sent>
Color : <Blue, Green, Black, Red>
How do you implement it?
The class should not be inherited
The instances are limited: no further instances
final class Color{
public static final Color Black = new Color(1);
public static final Color Blue = new Color(2);
public static final Color Green = new Color(3);
public static final Color Red = new Color(4);
private int color;
private Color(int i) {
this.color = i;
}
}
Java introduces enumerations for this purpose
Enumerated Data Type
A simple class
enum keyword instead of class or interface
Comma seperated enum instances
enum instances are constant
enum Color {
Black, Blue, Green, Red
}
enum Color {
Black, Blue, Green, Red
}
final class Color{
public static final Color Black = new Color();
public static final Color Blue = new Color();
public static final Color Green = new Color();
public static final Color Red = new Color();
}
enum Shape {
Rectangle, Circle, Square
}
enum StudentType{
BS, MS, PhD
}
Color color = Color.Black;
Shape shape = Shape.Circle;
show(shape, color);
...
private static void show (Shape shape, Color color) {
//show a shape with this color
...
}
enum types are implicitly final
Can not be a super-class
Because they declare constants that should not be modified
Instances are constants
enum constants are implicitly public, static and final
No new instances can be created
object instantiation of enum types with operator new results in a compilation error.
Enum can be a more complex class
With many constructors
And fields
And methods
enum Shape {
Rectangle(1), Circle(2), Square(3);
private int number;
Shape(int i){
number = i;
}
public int getNumber(){
return number;
}
}
Shape shape = Shape.Circle;
print (shape.getNumber());
shape = Shape.valueOf("Rectangle");
print(shape.getNumber());
Shape[] shapesArray = Shape.values();
for (Shape s : shapesArray) {
print(s.name());
}
try{
shape = Shape.valueOf("Pyramid");
}catch(Exception exp){
print("Pyramid is not included in Shape");
}
In order to access static members
Qualify references with the class they came from.
For example:
double r = Math.sin(Math.PI * theta);
static import allows unqualified access to static members
without inheriting from the type containing the static members
import static java.lang.Math.PI;
or
import static java.lang.Math.*;
double r = sin(PI * theta);
An annotation is a special form of metadata
Added to Java source code
Classes, methods, variables, parameters and packages may be annotated
Unlike Javadoc tags, Java annotations can be reflective
They can be embedded in class files (byte code)
May be retained by the Java VM at run-time
Example
@Override, @Deprecated, @ SuppressWarnings, …
A style of programming that treats computation as the evaluation of mathematical functions
Eliminates side effects
Treats data as being immutable
Functions can take functions as arguments and return functions as results
Prefers recursion over explicit for-loops
Allows us to write easier-to-understand, more declarative, more concise programs than imperative programming
Allows us to focus on the problem rather than the code
Facilitates parallelism
Java 8 is the biggest change to Java since the inception of the language
Lambdas are the most important new addition
Java is playing catch-up: most major programming languages already have support for lambda expressions
A big challenge was to introduce lambdas without requiring recompilation of existing binaries
Enabling functional programming
Writing leaner more compact code
Facilitating parallel programming
Developing more generic, flexible and reusable APIs
Being able to pass behaviors as well as data to functions
Syntax of Java 8 lambda expressions
Functional interfaces
Method references
Default methods
List<Integer> intSeq = Arrays.asList(1,2,3);
intSeq.forEach(x -> System.out.println(x));
List<Integer> intSeq = Arrays.asList(1,2,3);
intSeq.forEach(x -> {
x += 2;
System.out.println(x);
});
List<Integer> intSeq = Arrays.asList(1,2,3);
intSeq.forEach(x -> {
int y = x * 2;
System.out.println(y);
});
List<Integer> intSeq = Arrays.asList(1,2,3);
intSeq.forEach((Integer x -> {
x += 2;
System.out.println(x);
});
The Java 8 compiler first converts a lambda expression into a function
It then calls the generated function
For example, x -> System.out.println(x) could be converted into a generated static function
But what type should be generated for this function? How should it be called? What class should it go in?
public static void genName(Integer x) {
System.out.println(x);
}
Design decision: Java 8 lambdas are assigned to functional interfaces.
A functional interface is a Java interface with exactly one non-default method. E.g.,
The package java.util.function defines many new useful functional interfaces.
public interface Consumer<T> {
void accept(T t);
}
public interface Consumer<T> {
void accept(T t);
}
void forEach(Consumer<Integer> action {
for (Integer i:items) {
action.accept(t);
}
}
List<Integer> intSeq = Arrrays.asList(1,2,3);
Consumer<Integer> cnsmr = x -> System.out.println(x);
intSeq.forEach(cnsmr);
The method generated from a Java 8 lambda expression has the same signature as the method in the functional interface
The type is the same as that of the functional interface to which the lambda expression is assigned
The lambda expression becomes the body of the method in the interface
Method references can be used to pass an existing function in places where a lambda is expected
The signature of the referenced method needs to match the signature of the functional interface method
Method Reference Type | Syntax | Example |
---|---|---|
static | ClassName::StaticMethodName | String::valueOf |
constructor | ClassName::new | ArrayList::new |
specific object instance | objectReference::MethodName | x::toString |
arbitrary object of a given type | ClassName::InstanceMethodName | Object::toString |
We can rewrite the statement
intSeq.forEach(x -> System.out.println(x));
more concisely using a method reference
intSeq.forEach(System.out::println);
The new java.util.stream package provides utilities to support functional-style operations on streams of values.
A common way to obtain a stream is from a collection:
Stream<T> stream = collection.stream();
Streams can be sequential or parallel.
Streams are useful for selecting values and performing actions on the results.
An intermediate operation keeps a stream open for further operations. Intermediate operations are lazy.
A terminal operation must be the final operation on a stream. Once a terminal operation is invoked, the stream is consumed and is no longer usable.
filter excludes all elements that don’t match a Predicate.
map performs a one-to-one transformation of elements using a Function.
A stream pipeline has three components:
A source such as a Collection, an array, a generator function, or an IO channel;
Zero or more intermediate operations; and
A terminal operation
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
List<Integer> list = Arrays.asList(1,2,3);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);