Does anybody really know what time it is? (Does anybody really care? Is it 25 or 6 to 4?)
If you are working with dates and times, please understand time zones and ZoneId. That class is in the package java.time; oh, by the way, since we are all on Java 8 or later, please use java.time in new work. If you must work in Java 6 or 7, Joda Time is acceptable. Please purge any use of java.util.Date and java.util.Calendar from your code as soon as practical.
As for writing your own date/time handling code, I have one word of advice: Don’t. It’s much, much harder than it looks. (If only the engineers who developed java.util.Date had understood that simple fact…) Here’s an entertaining and educational video describing the issues.
Note: ZoneId in Java corresponds to the zones defined in /usr/share/zoneinfo on Unix systems.
One of the key concepts of object-oriented programming is Encapsulation, and classes are the main structures used to encapsulate implementation details. Java allows several ways to encapsulate within classes.
It is possible, and sometimes desirable, to enclose one class in another. Consider an object representation of a database record, that also defines a key from multiple fields. For example, here is a naive employee record:
class Person {
String lastName;
String firstName;
String title;
Person reportsTo;
Set<Person> directReports;
class ID {
String last;
String first;
}
}
The inner class ID could define the primary key for the database table. (Of course, in the real world, one would probably use an identifier field, rather than people’s names; it’s possible for people to have the same name, or for names to change, especially after a major life event.)
All classes may have methods, including enclosed classes. Methods in the enclosed class have access to fields and methods in the enclosing class, including those with private access. Note that the fields in Person.ID have different names from those in Person. This was to avoid shadowing, where a name in the inner scope hides a field with the same name in the outer scope.
A class may be declared inside a method. This allows the programmer to take logic out of the parent class and encapsulate it.
class Foo {
public void foo() {
class Bar {
doSomething() {
// something wonderful
}
}
Bar baz = new Bar();
baz.doSomething();
}
}
The object baz is, as expected, local to the method foo(). But so is its class, Bar. This prevents Bar from being used elsewhere, even in other methods of Foo.
Local classes have some access to their enclosing scope (subject to shadowing):
An anonymous class is an expression that implements an interface or extends a class. Consider the following trivial example:
class Main {
interface BinaryOperation<T extends Number> {
T doIt(T left, T right);
}
public void operations() {
BinaryOperation<Float> fAdd = new BinaryOperation<>() {
Float doIt(Float left, Float right) { return left + right; }
}
BinaryOperation<Integer> iMul = new BinaryOperation<>() {
Integer doIt(Integer left, Integer right) { return left * right; }
}
System.out.println("π + e = " + fAdd.doIt(3.14, 2.718));
System.out.println("3 * 2 = " + iMul.doIt(3, 2));
}
}
fAdd and iMul are objects of anonymous classes that implement BinaryOperation<>.
An anonymous class is an expression with the following syntax:
An anonymous class is a special case of a local class. Anonymous classes have the same access rules as other enclosed classes.
Starting with Java 8, lambda expressions were added to the language. A lambda takes one or more arguments and returns a value, similar to a method. The simplest lambda looks like this:
parameter -> expression
To create a lambda with multiple parameters, use parentheses:
(param1, param2) -> expression
To create a lambda with code that cannot be written in a single expression, use { curly braces }:
parameter -> { code block }
For example, consider a method to print all strings in a list:
public <T> void printAll(List<T> list) {
for (T elem : list) {
System.out.println(elem);
}
}
This can be rewritten as:
public <T> void printAll(List<T> list) {
list.forEach(elem -> System.out.println(elem));
}
If the lambda needs to return a value, the code block should have a return statement. (For the single expression lambda, the value of the expression is the returned value.)
Lambdas have the same access to the enclosing scope as local classes.
A lambda expression can be stored in a variable of type Consumer<T>, where T is the type returned by the lambda.
Lambdas can be stored in other interface types, such as Predicate<T>. A Predicate is a function that examines each element of a stream and returns a boolean, which if true means the element is to be included. Example:
public Pair<List<String>, List<String>> getOddsAndEvens(List<String> titles) {
List<String> odds = new ArrayList<>();
for (String title : titles) {
if ((title.length() & 1) != 0) {
odds.add(title);
}
}
List<String> evens = titles.stream()
.filter(s -> (s.length() & 1) == 0)
.collect(Collectors.toList());
return new Pair(odds, evens);
}
This method takes a list of strings, and splits them by whether the string length is odd or even. The odds are found in the for loop, while the evens are found with the predicate passed to filter(). (Of course, in the real world, we would use a single if and else to do this separation.)
To see examples of methods that takes a lambda, either in Consumer<T> or Predicate<T>, see the interface Stream<T>.