- Sir Tony Hoare
Types don't default nullable.
unsafe code is caught at compilation.
Once open, a class can never be closed.
Other code becomes dependent.
Design and document for inheritance or else prohibit it. (#14)
I can't add my utility methods.
BUT
I can depend on your class signature so it breaks my code.
myDate.isItAfterTheDeadline()
myActivity.showSnackBarMessage()
myString.pigLatinize()
No boilerplate. Your compiler handles it.
Design patterns are a symptom that your language is broken.
-- Jeff Atwood
Singletons?
BUILT IN!
Delegation?
BUILT IN!
Type-Safe Builders?
HOT DAMN!
Async/Await? Coroutines?
NOW WE'RE COOKING WITH GAS!
Checked Exceptions:
A Failed Psychology Experiment
try { doSomething(); }
catch (HorrificException e) {
// TODO write an error handler
// maybe after writing my novel,
// winning a Guinness record, &
// learning the digits of pi
}
https://goo.gl/qKYLhf
// Just import and call as static methods!
// Kotlin:
package demo
class Foo
fun bar() {}
// Java:
new demo.Foo();
demo.Foo.bar();
// use @JvmField annotation to expose fields
// boxed Java types map to Nullable types i.e. String?// Just import and call it!
import java.util.Calendar
fun calendarExample() {
val calendar = Calendar.getInstance()
}
// Compile-time null checks are reduced for Java code
// without nullability annotations. Duh!public class Customer {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
if (name != null ? !name.equals(customer.name) : customer.name != null) return false;
return email != null ? email.equals(customer.email) : customer.email == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (email != null ? email.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
public Customer copy() {
Customer copy = new Customer();
copy.name = name;
copy.email = email;
return copy;
}
}data class Customer(val name: String,
val email: String)
// DONE!
//
// This created:
// getters and setters
// equals()
// hashcode()
// toString()
// copy()
// and copy() with changed fields
// among other functions public boolean isCustomerSaved(String name, String email) {
// ...
}
public boolean isCustomerSaved(String email) {
isCustomerSaved("Default", email);
}
public boolean isCustomerSaved() {
isCustomerSaved("Default", "default@example.com");
}
public boolean isCustomerSaved(String name) {
isCustomerSaved(name, "default@example.com);
}fun isCustomerSaved(name: String = "Default",
email: String = "default@example.com") {
//...
}// Java 7
Iterator<Foo> it = collection.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
// Java 8
collection.stream()
.filter(p -> condition)
.collect(Collectors.toList());// long form
list.filter { x -> x > 0 }
// short form
list.filter { it > 0 }String string = String.format("Name %s", name);
System.out.println(string);println("Name $name")// if then version
if (x instanceof Foo) {
((Foo)x).fooMethod();
} else if (x instanceof Bar) {
((Bar)x).barMethod();
} else {}
// evil reflection, just don't
CLAZZ z = CLAZZ.valueOf(x.getClass().getSimpleName());
switch (z) {
case "Foo":
((Foo)x).fooMethod();
break;
case "Bar":
((Bar)x).barMethod();
break;
default:
}when (x) {
is Foo -> x.fooMethod()
is Bar -> x.barMethod()
else -> castException()
}
// no need to cast 'x' to call methods
// that's called smart castIterator it = mp.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
}for ((k, v) in map) {
println("$k -> $v")
}List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
val newList = listOne + listTwomyList.sort(new Comparator<MyObject>() {
@Override
public int compare(MyObject left, MyObject right) {
return left.getDate().compareTo(right.getDate());
}
});val sortedList = myList.sortedBy { it.date }public class Pair<T, U> {
public final T t;
public final U u;
public Pair(T t, U u) {
this.t= t;
this.u= u;
}
}
Pair<String, Int> getResult() {
// ...
return new Pair<String, Int>();
}
Pair<String, Int> myPair = getResult();data class Result(val result: Int,
val status: Status)
fun getResult(): Result {
// ...
return Result(result, status)
}
val (result, status) = getResult()
// This is called a destructuring declaration.
// It accepts an arbitrary number of parameters.// umm, import Guava with 15,000 Android-busting methods?
import com.google.common.collect.ImmutableList;
ImmutableList<String> myList = ImmutableList.of("a", "b", "c");val list = listOf("a", "b", "c")
// this will disallow modification at compile time
// but Java code can still modify it at run time
// it's a read only view of a mutable collection
// with compile-time write protection
// for truly immutable lists, use Guava// umm, import Guava with 15,000 Android-busting methods?
import com.google.common.collect.ImmutableMap;
ImmutableMap<String,String> myMap = ImmutableMap.of(
"key1", "value1",
"key2", "value2",
"key3", "value3");val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// same exception applies
// this is a read only view of a mutable map
// with compile time write protectionprivate int p = null;
// later in the code...
alterP();
public void alterP() {
if (p == null) {
p = computeP();
}
}val p: String by lazy {
computeP()
}public class StringWrapper extends String {
// FATAL ERROR: Cannot inherit from final java.lang.String
}
// okay try again...
public class StringUtil {
public static String spaceToCamelCase(String string) {
// ... this sucks!
}
}
StringUtil.spaceToCamelCase(string);
// How many util classes do I have to import?fun String.spaceToCamelCase() { ... }
"Convert this to camelcase".spaceToCamelCase()public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
if(instance == null){
synchronized (ThreadSafeSingleton.class) {
if(instance == null){
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
// This breaks on reflection, serializable, and rare situations.
// There is no one canonical singleton.object Resource {
val name = "Name"
}if (data != null) {
System.out.println(String.format("%s", data.get("email")));
} else {
System.out.println("default");
}
// OR...
System.out.println(data != null ?
String.format("%s", data.get("email"))
: "default");println(data?.email ?: "default")String call() {
if (something()) {
return doSomething();
else {
throw new UnsupportedOperationException("TODO: DoSomethingElse?");
}
// Thanks to Jake Wharton for the tips going forward!fun call : String {
if (something()) {
return doSomething()
else {
TODO("DoSomethingElse?") // exception
}public static String join(String sep, List<String> strings) {
if (sep == null) {
throw new NullPointerException("sep == null");
}
if (sep.length < 2) {
throw new IllegalArgumentException("sep.length < 2:" + sep);
}
if (strings == null) {
throw new NullPointerException("strings == null");
}
// ...
}fun join(sep: String, strings: List<String>) {
require(sep.length < 2) {"sep.length < 2: " + sep}
// ...
}
// there are other built-in validation functions like:
// requireNotNull(),
// check(),
// checkNotNull(),
// and assert()long startTime = System.currentTimeMillis();
doSomething();
long endTime = System.currentTimeMillis();
System.out.println(
String.format("took %sms", endTime - startTime)
);val timeInMillis = measureTimeMillis{doSomething()}
println("took ${timeInMillis}ms")
// function is inlined and fast
// there's also measureTimeNanos()@Deprecated
public static String join(String sep, List<String> strings) {
// ...
}
// The JavaDoc leaves you dying for documentation@Deprecated("Use strings.joinToString(sep).")
fun join(sep: String, strings: List<String>) {
// ...
}
// Mandatory string directs user to better option.
// Can set DeprecationLevel = ERROR or
// DeprecationLevel = HIDDEN to keep
// existing code working, but stop new users.// use automated IDE suggestion-based replacement
@Deprecated("Use strings.joinToString(sep).",
replaceWith =
ReplaceWith("strings.joinToString(sep)"))
fun join(sep: String, strings: List<String>) {
// ...
}
// even works with imports!
@Deprecated("Use Guava's joiner.",
replaceWith =
ReplaceWith("Joiner.on(sep).join(strings)",
imports = "com.google.common.base.Joiner"))
// ...class CopyPrinter implements Copy, Print {
private Copy copier;
private Print printer;
public CopyPrinter(Copy copier, Print printer) {
this.copier = copier;
this.printer = printer;
}
@Override
public Page copy(Page page) {
return copier.copy(page);
}
@Override
public void print(Page page) {
printer.print(page);
}
}
interface Copy {
Page copy(Page page);
}
interface Print {
void print(Page page);
}
class Page {}class CopyPrinter(copier: Copy, printer: Print)
: Copy by copier, Print by printer
interface Copy {
fun copy(page: Page): Page
}
interface Print {
fun print(page: Page)
}
class Page