Tuesday, March 18, 2014

Java 8 is the biggest thing to happen to Java since Java 7

This is a transcript of the keynote given by Larry Ellison at the EclipseCon 2014.

Good morning.
[ Applause ]
[ Cheering ]
Thank you for coming this morning.

We've got some really cool stuff to show you. It's an amazing time at Oracle, an extraordinary time and I'd like to show you some of the things going on, I'll start with a few updates beginning with the new date and time API.

It's in the package java.time. We spent 18 years working every detail of the date and time API, getting everything exactly right. We used immutable classes so your programs that don't use threads are absolutely thread safe now. We provided a fluent interface so you can chain your method calls to make your programs look stunning.

No one would have done this but Oracle ... and the guy who did JodaTime.

Here's how you get the first Sunday of the next month:
import java.time.DayOfWeek;
import java.time.LocalDate;
import static 
     java.time.temporal.TemporalAdjusters.firstDayOfNextMonth;
import static java.time.temporal.TemporalAdjusters.nextOrSame;

LocalDate firstSundayOfNextMonth = LocalDate
  .now()
  .with(firstDayOfNextMonth())
  .with(nextOrSame(DayOfWeek.SUNDAY));
LocalDate.with method is remarkable.

Let's format a date:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

LocalDate tomorrow = LocalDate.now().plusDays(1);
tomorrow.format(DateTimeFormatter.ISO_DATE); // "2014-03-06"
It's absolutely gorgeous.

We've put your favorite formats into the DateTimeFormatter class:
import java.time.format.DateTimeFormatter;

tomorrow.format(DateTimeFormatter.ISO_DATE); // "2014-03-06"
tomorrow.format(DateTimeFormatter.ISO_WEEK_DATE); // "2014-W10-5"
tomorrow.format(DateTimeFormatter.ISO_ORDINAL_DATE); // "2014-066"
LocalDate class has a signature of factory method and this method has a signature (int, int, int) or (int, java.time.Month, int):
import java.time.LocalDate;
import java.time.Month;

LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate sameDate = LocalDate.of(2014, Month.MARCH, 18);
All right.
[Cheering & Applause]
Now these snippets don't quite capture the energy of the brand new date and time API. This snippet does:
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

LocalDateTime now = LocalDateTime.now();
LocalDateTime afterNow = now.plusHours(5).plusMinutes(61);

long minutes = ChronoUnit.MINUTES.between(now, afterNow); // 361
long hours = ChronoUnit.HOURS.between(now, afterNow); // 6
long centuries = ChronoUnit.CENTURIES.between(now, afterNow); // 0
We're really excited about the ChronoUnit class. It's the most advanced way to calculate the amount of time between two temporal objects.

Interop with java.util.Date is incredibly simple. Here I just call toInstant, then atZone, and finally toLocalDate.
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

Date oldDate = new Date(114, 2, 18); // deprecated since 1.1
LocalDate brandNewDate = oldDate.toInstant()
  .atZone(ZoneId.systemDefault())
  .toLocalDate(); // 2014-03-18

Date oldDateFromBrandNewDate = Date.from(brandNewDate
  .atStartOfDay()
  .atZone(ZoneId.systemDefault())
  .toInstant()); // 2014-03-18
So that's a brief update on date and time API.

Now on to the stats.

Last year was an incredible year for Java. 89% of computers in the US run Java. 3 billion mobile phones run Java. Maven repository has over 600,000 artifacts. This is amazing.

No one could have predicted this. Now, today, we're taking it to the next level making a huge leap.
We have some very exciting news to tell you about Java.

We launched the first version of Java in 1996. And what an incredible breakthrough it was for the entire industry. Java simply went on to become the number one programming language in the world. That's why we called it Java one.

And each and every year, we introduced new versions with new features, new innovations. Each time, setting a new bar for what is the gold standard in the programming languages.

And we're going to do that again today. Today, we're going to introduce Java 8.
[ Applause ]
And I'd like to show it to you right now. So let's take a look at the brand new Java 8.

So, Java 8, it is made entirely of lambdas and streams. It's designed to an exacting level of standard unlike anything we or anyone in our industry has made before.

And I don't think it is an exaggeration to say that the software engineering that has gone into this product is the most challenging our team has even taken on. And what they've accomplished is simply amazing.

So let's walk through it.

Java 8 is the first programming language in history that has lambdas. But before we get into it, this is the monumental challenge the team had. How do you add lambdas to a language that doesn't even have the function type? You reinvent the interfaces. Interfaces in Java 8 are absolutely beautiful. Java 8 interface can provide a default method implementation:
public interface Language {
  default boolean hasLambdas() {
    return false;
  }
}
With typical legendary Java ease of use you just add the default modifier, provide an implementation and it works.

But we didn't stop there. We've added static methods to the interfaces.
public interface Language {
  static int getNumberOfLanguagesThatRunOnThreeBillionDevices {
    return 1;
  }
}
To give the finishing touch to the interfaces we've added stunning @FunctionalInterface annotation.

Functional interface is an interface that has exactly one abstract method.
@FunctionalInterface
public interface Language {
  default boolean hasLambdas() {
    return false;
  }

  static int getNumberOfLanguagesThatRunOnThreeBillionDevices {
    return 1;
  }

  // abstract method
  boolean runsOnThreeBillionDevices();
}
We don't want to make Java 8 too restrictive and @FunctionalInterface annotation is not required to make the interface functional. Having exactly one abstract method in the interface is enough.

But if you provide the @FunctionalInterface annotation, then our brand new compiler won't compile a functional interface with two or more abstract methods. It works like magic.
// Compilation error: Language is not a functional interface.
// Multiple non-overriding abstract methods found 
// in the interface Language
@FunctionalInterface
interface Language {
  boolean runsOnThreeBillionDevices();
  boolean isAmazing();
}
It's amazing. Let's use the brand new default methods to fix this error:
@FunctionalInterface
public interface Language {
  boolean runsOnThreeBillionDevices();

  default boolean isAmazing() {
    return runsOnThreeBillionDevices();
  }
}
But why would we redesign interfaces that way? And what does it have to do with lambdas?
Lambdas should feel great in the language and more importantly, should be easy to use. So when you have a functional interface you can instantiate it with lambda.

This is simply amazing.
Language java = () -> true;
System.out.println(java.runsOnThreeBillionDevices()); // true
When you instantiate a functional interface with lambda then this lambda "substitutes" that single abstract method. Type signatures of lambda and abstract method should match.

What's really amazing is that many of the JDK interfaces are in fact functional interfaces and can be instantiated with lambdas.

Working with threads and Runnable was never so much fun:
(new Thread(() -> System.out.println("Awesome"))).start();
Writing custom comparators is incredibly simple:
import java.util.Comparator;

Comparator<String> lengthComparator = 
  (x, y) -> Integer.compare(x.length(), y.length());
It's even simpler with the Comparator's brand new helper methods and method references:
import java.util.Comparator;

Comparator<String> lengthComparator = 
  Comparator.comparing(String::length);
Java 8 lambdas come in several different shapes. Our favorite at Oracle is type-inferenced short version. You just write an argument list, an arrow (->), and provide and implementation. It's pretty awesome:
Comparator<String> lengthComparator = 
  (x, y) -> Integer.compare(x.length(), y.length());
If you want to you can add a type signature:
Comparator<String> lengthComparator = 
  (String x, String y) -> Integer.compare(x.length(), y.length());
Or explicit return statement:
Comparator<String> lengthComparator = (String x, String y) -> { 
    return Integer.compare(x.length(), y.length()); 
  };
Method references can be used instead of lambdas with stunning effect.
// static method reference
// same as (int x, int y) -> Integer.compare(x, y)
Comparator<Integer> integerComparator = Integer::compare;

// non-static method reference
// same as (Integer integer) -> integer.byteValue()
Comparator<Integer> intAsByteComparator = 
  Comparator.comparing(Integer::byteValue);

// instance method reference 
// same as (String suffix) -> javaString.endsWith(suffix)
String javaString = "Java 8";
Comparator<String> mysteriousComparator = 
  Comparator.comparing(javaString::endsWith);

// constructor reference, same as (String s) -> new Integer(s)
Comparator<String> stringAsIntegerComparator = 
  Comparator.comparing(Integer::new);


Next, ultrauseful java.util.stream package. It plays so nice with lambdas.

It's nothing like seeing it. So I'd like to show it to you now.
import java.util.stream.Stream;
Creating a stream is remarkably simple:
Stream<String> languages = Stream.of("C", "Java 8", "C++", 
                                     "Haskell", "C");
Let's filter out high-level languages:
languages.filter(lang -> lang.startsWith("C")); 
// stream of C, C++, C
Method references feel incredible with the streams:
languages.map(String::length); // stream of 1, 6, 3, 7, 1
Stream.map applies specified function to the elements of the stream and returns a new stream.
Here's one gotcha though. You can chain stream methods:
languages.filter(lang -> lang.startsWith("C")).map(String::length);
But you can't call two methods on the same stream. This won't work:
languages.filter(lang -> lang.startsWith("C"));
languages.map(String::length); // throws IllegalStateException
We didn't stop with the map method. We've added mapToInt, mapToLong, and mapToDouble methods so you can enjoy working with your favorite primitive types:
languages.mapToInt(String::length) 
// primitive stream of 1, 6, 3, 7, 1. We call it IntStream
Stream.distinct method is fantastic when you don't want duplicate elements:
languages.filter(lang -> lang.startsWith("C")).distinct(); 
// stream of C and C++
We've reinvented the sorting. It's so simple:
languages.sorted(); // C, C, C++, Haskell, Java 8
Brand new Comparator methods take sorting to a different level:
languages.sorted(Comparator.reverseOrder()); 
// Java, Haskell, C++, C, C
Limiting and skipping your streams is incredibly fun:
languages.limit(3); // C, Java 8, C++
languages.skip(2); // C++, Haskell, C
Next, gorgeous forEach method:
languages.forEach(System.out::println); // println every element
By the way we've added the forEach method to our classic Iterable interface. It's fantastic.

How do you convert a stream to array? It's easy. You just call the toArray method:
String[] langs = languages.toArray(String[]::new); 
// convert to a string array
Counting elements is so easy:
languages.count(); // 5
These methods are so intuitive and natural I won't even explain what they're doing:
languages.anyMatch(s -> s.equals("Java 8")); // true
languages.allMatch(s -> s.equals("Java 8")); // false
languages.noneMatch(s -> s.equals("Java 8")); // false
Parallel processing of streams works like magic. You just call the parallel method and continue working with the stream like nothing happened:
languages.parallel().forEach(System.out::println); 
// makes processing parallel
Concat method is great for concatenating streams:
Stream.concat(Stream.of("Java", "Java 8"), Stream.of("C", "C++")); 
// Stream of Java, Java 8, C, C++
Finding the maximum element is gorgeous:
languages.max(Comparator.naturalOrder()); // Optional[Java 8]
Getting the first element is at least five years ahead of what's in any other language:
languages.findFirst(); // Optional[C]
Now, these last two methods return an Optional<String>. What is Optional? Optional is an amazing container which may or may not contain a non-null value.

You create an Optional with the revolutionary of method:
import java.util.Optional;

Optional<String> os = Optional.of("Java 8");
Creating an empty Optional is so easy with the empty method:
Optional<String> empty = Optional.empty();
You get a value from an Optional with the get method:
os.get(); // "Java 8"
To check if an Optional contains a value, you use the isPresent method:
os.isPresent(); // true
empty.isPresent(); // false
Optional.filter method is fantastic. If the filter predicate returns true for Optional value then it returns the Optional itself. If the filter predicate returns false then it returns an empty Optional.
os.filter(s -> s.startsWith("J")); // Optional[Java 8]
os.filter(s -> s.equals("C++"))); // Optional.empty
Filter method works for an empty Optional as well. It simply returns an empty Optional.
empty.filter(s -> s.startsWith("J")); // Optional.empty
Optional.map method is so cool. It applies a function to an Optional value and returns a new Optional.
os.map(s -> s + " is the best Java we've ever made"); 
// Optional[Java 8 is the best Java we've ever made]
As you can guess it works for an empty Optional as well:
empty.map(s -> s + " is the best Java we've ever made"); 
// Optional.empty
Optional.orElse method returns an Optional value or the default value if Optional is empty.
os.orElse("C++"); // Java 8
empty.orElse("Java 8"); // Java 8
But we didn't stop with the Optional<T>. We've added the OptionalInt, OptionalLong, and OptionalDouble classes so Java 8 has now 3 Optional types more than Haskell.

We've expanded your favorite JDK classes to take advantage of the fantastic streams API.

Files.lines method is the most advanced way to get all lines in a file.
import java.nio.file.Files;
import java.nio.file.Paths;

Stream<String> lines = Files.lines(Paths.get("Java.java"));
We've also added lines method to the BufferedReader class:
import java.io.BufferedReader;
import java.nio.file.Files;
import java.nio.file.Paths;

BufferedReader reader = Files.newBufferedReader(
                          Paths.get("Java.java"));
Stream<String> lines = reader.lines();
By the way Files.newBufferedReader is new in Java 8. There is also of course Files.newBufferedWriter.

We've added the stream method to the Collection interface:
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;

Collection<String> languagesCollection = new ArrayList<>();
languagesCollection.add("Java 8");
languagesCollection.add("C++");
Stream<String> languagesStream = languagesCollection.stream();
It's the best way to convert a collection to a stream.
java.util.Arrays utility class also has the stream method:
import java.utils.Arrays;

int[] intNumbers = new int[] { 8, 9, 10};
    Arrays.stream(intNumbers); // stream of integers
The stream method is overloaded so it works with all your favorite types. Unless your favorite type is byte.
import java.utils.Arrays;

double[] doubleNumbers = new double[] { 2.71, 3.14, 69.69};
String[] strings = new String[] { "Java 6", "Java 7", "Java 8"};
Arrays.stream(doubleNumbers).average(); // OptionalDouble[25.18]
Arrays.stream(strings).filter(s -> s.endsWith("8")).findFirst() 
// Optional[Java 8]
While we're at Arrays. Java 8 is 100% ready for the multicore revolution with the fantastic Arrays.parallelSort method:
import java.utils.Arrays;

double[] doubleNumbersToSort = new double[] { 69.69, 2.71, 3.14, };
Arrays.parallelSort(doubleNumbersToSort); // in-place sort: 2.71, 3.14, 69.69
Removing nulls from Collection is so simple with our brand new removeIf and Objects.isNull methods:
import java.util.Objects;
languagesCollection.removeIf(Objects::isNull); 
// modifies collection in-place
Pure stream solution is also amazing:
languagesStream.filter(Objects::nonNull); 
// returns a new stream with non null objects.
It's incredible.

While we're at collections, List interface has the stunning sort method now:
import java.util.ArrayList;
import java.util.Comparator;

ArrayList<String> languagesList = new ArrayList<>();

languagesList.add("Java 8");
languagesList.add("C++");
languagesList.add(null);
languagesList.sort(
  Comparator.nullsFirst(Comparator.naturalOrder())); 
// null, C++, Java 8
You can handle nulls in the list by using Comparator.nullsFirst method. No exception is thrown. It's amazing. Comparator also has the nullsLast method.

Random class has the ints, doubles, and longs methods that return streams:
import java.util.Random;

(new Random()).ints().limit(100); // stream of 100 random integers
(new Random()).ints(); // infinite stream of random integers
Now that's all great and amazing but I hear you asking what about the argument types? What is the argument type of the Stream.filter method? What is the argument type of the Stream.map method?

Let's look at the source code of our brand new Stream class:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Stream<T> filter(Predicate<? super T> predicate);
The team has added Function and Predicate types. Function and Predicate are of course functional interfaces and you can instantiate them with lambdas. That's why streams are so fun in Java 8. But the team didn't stop there. They've added some fantastic methods to the Function and Predicate interfaces.

It starts of course with the Function.compose method - the best way to compose functions:
import java.util.function.Function;

Function<String, String> toUpper = String::toUpperCase;
Function<String, String> concatAndToUpper = toUpper
  .compose(s -> s + " is the best Java we've ever made");
concatAndToUpper.apply("Java 8"); 
// JAVA 8 IS THE BEST JAVA WE'VE EVER MADE
And then there's amazing andThen method that constructs a new function with a slight twist:
Function<String, String> toUpperAndConcat = toUpper
  .andThen(s -> s + " is the best Java we've ever made");
toUpperAndConcat.apply("Java 8"); 
// JAVA 8 is the best Java we've ever made
Static method Function.identity can be helpful at lazy Sunday mornings when you don't want to do anything:
import java.util.function.Function;

Function<String, String> returnTheArgument = Function.identity();
returnTheArgument.apply("Java 8"); // Java 8
And that was the Function class. But there's so much more.

The Predicate class is stunning. Predicate is basically a function that takes one argument and returns a boolean.
import java.util.function.Predicate;

Predicate<String> isAmazing = s -> s.equals("Java 8");
isAmazing.test("Java 8"); // true
isAmazing.test("Haskell"); // false
Predicate interface has some incredible default methods. Predicate.or is fantastic for chaining or conditions:
Predicate<String> isGreatOrAmazing = isAmazing
  .or(s -> s.equals("Java 7"));
isGreatOrAmazing.test("Java 7"); // true
It also has the similar and method. Predicate.negate is beautiful:
Predicate<String> isNotAmazing = isAmazing.negate();
isNotAmazing.test("Java 7"); // true
isNotAmazing.test("Java 8"); // false
My favorite Predicate method is the static method isEqual. It returns a new Predicate that checks for equality to a specified object. It plays so nice with the brand new stream API:
import java.util.stream.Stream;
import static java.util.function.Predicate.isEqual;

Stream.of("C", "Java 8", "C++").filter(isEqual("Java 8")); 
// Stream of Java 8
Predicates are so useful that we've added the asPredicate method to the java.util.regex.Pattern class.
import java.util.regex.Pattern;
import java.util.stream.Stream;

Pattern isJava = Pattern.compile("^Java \\d+$");
Stream.of("Java 8", "C", "C++", "Java 7")
      .filter(isJava.asPredicate()); // Stream of Java 8, Java 7
And that was the new Predicate and Function interfaces. But the team didn't stop there.

They've enhanced the function types even further. They've added the first in history LGBT-friendly types BiFunction and BiPredicate. They're are similar to Function and Predicate but take two arguments instead of one. It's incredible.

But the team didn't stop there. They've added IntToLongFunction, IntToDoubleFunction, LongToIntFunction, LongToDoubleFunction, DoubleToIntFunction, DoubleToLongFunction, ToIntFunction, ToLongFunction, ToDoubleFunction, ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction, IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator, IntPredicate, LongPredicate, and DoublePredicate.

So you can work with all your favorite primitive types. That's by the way about 18 function types more than in Haskell. That's what Java 8 is all about: Java 8 is not a functional language, but it's so much better functional language than any functional language in the world.

[ Applause ]
Perhaps one of the most important features of Java is the JDK. And we have JDK 8, the latest version of the world's most advanced standard library. And it's been designed from the very beginning to take full advantage of this beautiful lambdas.

We are really excited about JDK 8. It has so many new features in it and I like to go ahead and show just a few of those for you here. We'll start off with our brand new java.util.StringJoiner class.

Let me go ahead and join some strings:
import java.utils.StringJoiner;

StringJoiner sj = new StringJoiner(", ");
sj.add("Java 8").add("C++").add("Haskell");
sj.toString(); // Java 8, C++, Haskell
And we also have this great constructor StringJoiner(String delimiter, String prefix, String suffix):
import java.utils.StringJoiner;

StringJoiner sj = 
  new StringJoiner(", ", "The most advanced languages are: ", ".");
sj.add("Java 8").add("C++").add("Haskell");
sj.toString(); 
// "The most advanced languages are: Java 8, C++, Haskell."
We've also got great Base64 class.
import java.util.Base64;

byte[] encodedMessage = Base64
  .getEncoder()
  .encode("Java 8 is the best Java we've ever made".getBytes());
new String(Base64.getDecoder().decode(encodedMessage)); 
// "Java 8 is the best Java we've ever made"
It's just gorgeous.

Next step, I'd like to show a nice little enhancement we made to the Math class. Now you can add numbers without worrying about silent integer overflow. Math.addExact adds two ints and throws ArithmeticException if sum overflows an int.
int maxValuePlusOne = Integer.MAX_VALUE + 1; 
// silent overflow, -2147483648

Math.addExact(Integer.MAX_VALUE, 1); // throws ArithmeticException
We've also added subtractExact, multiplyExact, incrementExact, decrementExact, and negateExact methods to the Math class. If your favorite integer type is long - every of the exact methods is overloaded on long.

It's that easy to safely work with numbers now.

The next step, let me show you some enhancements we've made to the Byte class.
First, let me convert a byte to an int:
// Java 7 way
byte theBiggestByteWeHaveEverMade = (byte)255;
int byteAsInt = theBiggestByteWeHaveEverMade & 0xFF;
This is the old way. And it's fine. But it's so much nicer with the brand new Byte.toUnsignedInt method:
int byteAsIntBrandNewWay = 
  Byte.toUnsignedInt(theBiggestByteWeHaveEverMade);
We've also provided Byte.toUnsignedLong method if your bytes are so big that they don't fit into an int.

Next, I'd like to show you some enhancements to the java.math.BigInteger. We've added the ultrasafe BigInteger.intValueExact method that throws an ArithmeticException if BigInteger doesn't fit into an int:
import java.math.BigInteger;

BigInteger big = BigInteger.valueOf(Integer.MAX_VALUE)
                           .add(BigInteger.ONE);
int bigAsInt = big.intValueExact(); // throws ArithmeticException
But we didn't stop there. We went on to add the byteValueExact, shortValueExact, and longValueExact methods so you can work with all your favorite primitive types. Even if your favorite primitive type is byte.
long bigAsLong = big.longValueExact(); // that's ok
Next, string joining. The team worked really hard at it and we have two join methods in the String class. The first one takes a delimiter and any number of strings.
String joinedString = String.join(", ", "Java 8", "Java 7", "C++", 
                                  "Haskell"); 
// "Java 8, Java 7, C++, Haskell"
Let me show you the second join method. It takes a delimiter and an Iterable.
import java.util.Arrays;
import java.util.List;

List<String> someStrings = Arrays.asList("Java 8", "Java 7", "C++", 
                                         "Haskell");
String joinedStringIterableWay = String.join(", ", someStrings); 
// "Java 8, Java 7, C++, Haskell"
Next, I'd like to show you a brand new class and that is java.util.stream.Collectors.
The Collectors class is the best way to collect all of your stream elements into one place.
Here I have a stream of strings:
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Stream<String> languages = Stream.of("C", "Java 8", "C++", 
                                     "Haskell", "C");
Let's convert this stream to a list. It's so easy with the Collectors.toList method.
List<String> asList = languages.collect(Collectors.toList());
List is of course an Iterable so our brand new forEach method is fantastic here:
asList.forEach(System.out::println);
Collectors.toSet is the most advanced way to convert a stream to a set.
Set<String> asSet = languages.collect(Collectors.toSet());
And one of the really cool things about Collectors is the joining method. Joining a stream of strings has never been that easy. No more Apache Commons, no more Guava, it's just Java 8 since today.
String joined = languages.collect(Collectors.joining(", ")); 
// String "C, Java 8, C++, Haskell, C"
Here I skip the delimiter argument and it just works:
String joinedWithoutDelimiter = languages
  .collect(Collectors.joining()); // String "CJava 8C++HaskellC"
What Collectors.groupingBy does is simply astounding:
Map<Character, List<String>> groupedByFirstChar = 

languages.collect(Collectors.groupingBy(s -> s.charAt(0))); 
// {'C': ["C", "C++", "C"], 'J': ["Java 8"], 'H': ["Haskell"]}
Let's print this map using the brand new Map.forEach method:
groupedByFirstChar.forEach(c, langs) -> 
  System.out.printf("%s=%s\n", c, String.join(", ", langs)));
Fantastic.

Collectors.partitioningBy is incredible for boolean grouping:
Map<Boolean, List<String>> paritioned = 
languages.collect(Collectors.partitioningBy(s -> s.startsWith("J"))); 
// {true: ["Java 8"], false: ["C", "C++", "Haskell", "C"]}
[ Applause ]
And that is Collectors.

Without doubt, JDK 8 is the most advanced standard library in the world and it powers Java 8.

The team has packed in innovation at every level of the design. It's the first language in the history that has lambdas. It's got more function types than any functional language. The brand new stream API would double the programmer productivity.

The all new date and time API, as well as Iterable.forEach method and that killer string joining feature.
It's got all new Optional type with the map method, and of course JDK 8.

Java 8 is the best Java we have ever made.

When you think about Java, it's probably the language that you use most at your work. It's the language that you have with you all the time. It powers Android, Hadoop, and Java Server Pages.

With this unique relationship people have with Java, we take changing it really seriously. We don't want to just make a new language. We want to make a much better language.

Java 8 is the result of this approach.

[ Applause ]

Well, I don't know about you but for me I think there's just two questions left. How much do I have to spend to get one?

Well, the great news, it's the same price as the Java 7 it replaces. It's completely free and comes with an awesome Ask Toolbar. That's amazing.

And the second question, when can I get my hands on one? Well, the great news it's getting out in the world today. Just check our downloads page.

[ Applause ]
Thank you.

Java 8, the biggest thing to happen to Java since Java 7. And I am so incredibly proud of everyone at Oracle that helps makes today occur. This language is simply amazing.

I hope you're as excited as we are by what you saw today. Thanks very much for coming.

1 comment: