Enhancements in Java 8 – Java
Programming
A. Lambda Expression
Aka – anonymous function – not bound to an identifier
1.
Used as arguments to higher-order functions
2.
Used to construct the result of a higher-order
function that needs to return a function
Functionality that need to be used
only for short term – e.g.: ‘closures’ and ‘currying’
a
=
['house', 'car', 'bike']
a.sort
(lambda
x,
y: cmp
(len(x), len(y)))
print (a)
['car', 'bike', 'house']
B. Default Methods
Aka – defender function – allows new methods to be added to
interfaces without breaking the functionality of existing interface.
Allows interface to use as default in the
situation where a concrete class fails to provide implementation for that
method.
public interface oldInterface {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method"
" is added in
interface");
}
}
How
we can use default methods?
ü
Not override the default method and will inherit
the default method.
ü
Override the default method similar to other
methods we override in subclass.
ü
Redeclare default method as abstract, which
force subclass to override it.
C. Repeating Annotations
Allows same annotations to be used
more than once to same declaration or type use. Prior to Java 8, to have
repeated annotations, we need to group them into an annotation container.
@Manufactures({
@Manufacturer(name =”BMW”),
@Manufacturer(name = “Range Rover”)
})
public class Car{
//code goes in here
}
Now with Java 8, for repeating
annotations, we need not specify an annotation container.
@Manufacturer(name = “BMW”)
@Manufacturer(name= “Range Rover”)
public class Car{
//code goes in here
}
D. Type Annotation
Java 7 allowed annotations to be written
only on method formal parameters and declarations
of packages, classes, methods, fields and local variables. Type annotations are
annotations that can be used anywhere we use a type.
Examples:
@NotNull String str1 = ...
@Email String str2 = ...
@NotNull @NotBlank String str3 = ...
Java annotations are used to provide meta
data for your Java code. Annotations are used for compiler instructions,
build-time instructions and runtime instructions.
Type annotations are provided for stronger
type checking.
Java 8 introduces annotations to ‘use’ of
types.
·
Class instance creation expression:
new @Intered MyObject();
·
Type cast
mystring = (@NonNull
String) str;
·
implements
clause
class UnmodifiableList<T> implements
@Readonly
List<@Readonly T> { ... }
·
Thrown exception declaration
void monitorTemperature() throws
@Critical
TemperatureException { ... }
E. Method References
Shortcuts that can be used anywhere we use
lambdas. They are compact and more readable
form of a lambda expression for already written methods. “::” operator is used for method reference.
Example:
interface IsReferable {
public void
referenceDemo();
}
class ReferenceDemo {
public static void commonMethod()
{
System.out.println("This
method is already defined.");
}
public void implement()
{ // Anonymous class.
IsReferable demoOne = new
IsReferable() {
@Override
public void
referenceDemo() {
ReferenceDemo.commonMethod();
}
};
demoOne.referenceDemo();
// Lambda implementaion.
IsReferable demo = () ->
ReferenceDemo.commonMethod();
demo.referenceDemo();
// Method reference.
IsReferable
demoTwo = ReferenceDemo::commonMethod;
demoTwo.referenceDemo();
}
}
F. Type Interface - Generics
It is possible to infer the generic types
from method signatures when passing a constructor as a parameter of a method.
Java 7 does not use ‘target typing’, but Java 8 does. Target typing is a
language feature wherein the type of the variable in which a result is to be
stored influences the type of the computation.
Target
typing example:
long MICRO_SECONDS_IN_DAY = 24 × 60 × 60 ×
1000 × 1000; à
Java 7 does not return what is expected.
Type interface example:
public class
TypeInference {
public
int getDictionarySize(Map<String, String> theDict) {
return
theDict.size();
}
}
@Test
public void
emptyDictionarySizeShouldBeZero() {
TypeInference
testObj = new TypeInference();
Map<String,
String> dict = new HashMap<>();
int
expected = 0;
int
actual = testObj.getDictionarySize(dict);
assertEquals("Size
is incorrect!", expected, actual);
}
@Test
public void
emptyDictionarySizeShouldBeZero2() {
TypeInference
testObj = new TypeInference();
int
expected = 0;
int actual =
testObj.getDictionarySize(new HashMap<>()); //Java 7: Compile
Error.
//Java
8: Better Type Inference
assertEquals("Size
is incorrect!", expected, actual);
}
G. Method parameter reflection
We can obtain the names of the formal
parameters of any method constructor with the method java.lang.reflect.Executable.getParameters.
However, .class files do
not store formal parameter names by default. To store formal parameter names in
a particular .class file,
and thus enable the Reflection API to retrieve formal parameter names, compile
the source file with the -parameters option
of the javac compiler.
H. Collections – The new ‘java.util.stream’ package
Classes in the new java.util.stream package provide a Stream API to support
functional-style operations on streams of elements. The Stream API is
integrated into the Collections API, which enables bulk operations on
collections, such as sequential or parallel map-reduce transformations.
Example:
int sum = widgets.stream()
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
Here we use widgets, a
Collection<Widget>, as a source for a stream, and then perform a
filter-map-reduce on the stream to obtain the sum of the weights of the red
widgets.
The key abstraction introduced in this
package is stream. The classes Stream, IntStream,
LongStream, and DoubleStream are streams over objects and the primitive
int, long and double types. Streams differ from collections in several ways:
No
storage. A stream is not a data structure that stores elements; instead, it
conveys elements from a source such as a data structure, an array, a generator
function, or an I/O channel, through a pipeline of computational operations.
Functional
in nature. An operation on a stream produces a result, but does not modify
its source.
Laziness-seeking.
Many stream operations, such as filtering, mapping, or duplicate removal, can
be implemented lazily, exposing opportunities for optimization.
Possibly
unbounded. While collections have a finite size, streams need not.
Consumable.
The elements of a stream are only visited once during the life of a stream.
Like an Iterator, a new stream must be generated to revisit the same elements
of the source.
Stream operations are divided into intermediate and terminal operations,
and are combined to form stream pipelines. A stream pipeline consists of a source (such as
a Collection, an array, a generator function, or an I/O channel); followed
by zero or more intermediate operations such
as Stream.filter or Stream.map; and a terminal operation such
asStream.forEach or Stream.reduce.
I.
Collections
- Performance Improvement for HashMaps with Key Collisions
Hash bins containing a large number of
colliding keys improve performance by storing their entries in a balanced tree
instead of a linked list. This JDK 8 change applies only to HashMap, LinkedHashMap, and ConcurrentHashMap.
In rare situations, this change could
introduce a change to the iteration order of HashMap and HashSet. A particular
iteration order is not specified for HashMap objects - any code that depends on
iteration order should be fixed.
Java 8 is on average 20% faster than Java 7
in simple HashMap.get(). When a bucket becomes too big (currently:
TREEIFY_THRESHOLD = 8), HashMap dynamically replaces it with an ad-hoc
implementation of tree map.
References:
No comments:
Post a Comment