On Github corani / advanced-java
enum Animal { CAT, DOG, MOUSE }
Animal animal = Animal.CAT; switch(animal) { case CAT: // do cat things break; case DOG: // do dog things break default: // do default things }
public class Animal { public static final Animal CAT = new Animal(); public static final Animal DOG = new Animal(); public static final Animal MOUSE = new Animal(); }
Animal.values() => Animal[]; Animal.valueOf(String s) => Animal; Animal cat = Animal.CAT; cat.name() => "CAT"
enum Animal { CAT("meow"), DOG("woof"), MOUSE("squeek"); private String sound; Animal(String sound) { this.sound = sound; } String getSound() { return sound; } } Animal cat = Animal.CAT; cat.getSound();
List strings = new ArrayList(); strings.add("hello"); strings.add("world"); // no problem! strings.add(42);
This works, until ...
for (Object o : strings) { // ... RuntimeException here! String s = (String) o; }
List<String> strings = new ArrayList<String>(); strings.add("hello"); strings.add("world"); // Compile-time exception! strings.add(42);
And when you use it ...
for (String s : strings) { // ... No need to cast! }
public Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } } public class Util { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2); }
public void print(List<Object> list) { for (Object elem : list) System.out.println(elem); } List<Integer> ii = new ArrayList<Integer>(); print(ii);
No! :-( Integer is not the same as Object, though it inherits from Object.
public void print(List<?> list) { for (Object elem : list) System.out.println(elem); } List<Integer> ii = new ArrayList<Integer>(); print(ii);
But now we throw out type-safety!If all objects that can be printed implement the "Printable" interface, we can do better.
public void print(List<? extends Printable> list) { for (Object elem : list) System.out.println(elem); } List<Integer> ii = new ArrayList<Integer>(); print(ii);
Confusing: extends is used for both classes and interfaces.
Suppose your method accepts a List that you plan to add numbers to. How to make sure you can't use other lists?
public void addNumbers(List<? super Integer> list) { for (int i = 0; i < 10; i++) list.add(i); }
Now you can supply a List of Integers, Numbers or Objects, but not a List of Strings or Widgets.
Because Generics were added to the language (in SE 1.5/5.0) and byte-code compatibility is an important feature of Java, all Generics are erased during compilation, after type-checking.
This is usually okay, but it limits the information you can get using reflection.
Don't implement them yourself, almost any type already exist!
If you really need to implement one yourself, start from one of the abstractXxx collection types.
Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "One"); map.put(2, "Two"); //map.put("Three", 3);
for (String value : map.values()) { // ... } for (Integer key : map.keySet()) { // ... } for (Map.Entry<Integer, String> entry : map.entrySet()) { entry.getKey(); entry.getValue(); }
class YourType { @Override public boolean equals(Object other); @Override public int hashCode(); }
class YourType implements Comparable<YourType> { @Override public int compareTo(YourType other) { // return -1, 0, +1 } }
public class Person { public static final Comparator<Person> ORDER_FIRSTNAME = new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.firstname.compareTo(p2.firstname); } }; public static final Comparator<Person> ORDER_AGE = new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.age - p2.age; } }; }
Collections.sort(persons, Person.ORDER_FIRSTNAME);
Every class has an associated meta-class that can be used to get all sorts of information about the class and to interact with the class.
Object.class; Object o = new Object(); o.getClass();
Besides that, there's a Class utility class (note the capitalization):
Class c = Class.forName("...");
try { Class c = Class.forName("com.example.Test"); Test t = (Test) c.newInstance(); } catch (ClassNotFoundException e) { // ... } catch (InstantiationException e) { // ... } catch (IllegalAccessException e) { // ... }
Many many possible exception!
// Get all the constructors: Constructor[] constructors = clazz.getDeclaredConstructors(); // Get the constructor matching a specific signature: Constructor constructor = clazz.getDeclaredConstructor(String.class); // Instantiate an object: Example example = (Example) constructor.newInstance("String");
// Get all the methods (public and private): Method[] methods = clazz.getDeclaredMethods(); // Get all visible methods (including from super-classes) Method[] methods = clazz.getMethods(); // Get the method for a specific signature: Method method = clazz.getDeclaredMethod("setExample", String.class); // Invoke the method on an object: method.invoke(example, "New String");
@Override public void someMethod() {} @SuppressWarnings public void badMethod() {} @Deprecated public void oldMethod() {}
Annotations provide information to the compiler, some can be made available at runtime through reflection.
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Shared { boolean restricted() default false; }
And use them like:
public class Users { @Shared public int getCount(); @Shared(restricted=true) public User getUserById(int id); public void hidden(); }
// Iterate over all the methods in the class for (Method m : Users.class.getMethods()) { Shared shared = m.getAnnotation(Shared.class); // If the annotation is present if (shared != null) { if (shared.restricted()) { // share privately } else { // share publicly } } }
public class MyRunnable implements Runnable { @Override public void run() { // ... } } Runnable r = new MyRunnable(); new Thread(r).start()
public class MyThread extends Thread { @Override public void run() { // ... } } new MyThread().start()
try { Thread.sleep(4000); } catch (InterruptedException e) { // ... }
while (true) { doHeavyWork(); if (Thread.interrupted()) { // ... } }
try { AnotherThread.join() } catch (InterruptedException e) { // ... }
The problem:
public class Counter { private int c = 0; public void inc() { c = c + 1; } public void dec() { c = c - 1; } }
One way to solve it:
public class Counter { private int c = 0; public synchronized void inc() { c = c + 1; } public synchronized void dec() { c = c - 1; } }
public class Counter { private AtomicInteger c = new AtomicInteger(); public void inc() { c.incrementAndGet(); } public void dec() { c.decrementAndGet(); } }
public void inc() { synchronized(this) { c = c + 1; } }
private Object lock = new Object(); public void inc() { synchronized(lock) { c = c + 1; } }
public class Friend { public synchronized void greet(Friend f) { System.out.println("Hi!"); f.greetBack(this); } public synchronized void greetBack(Friend f) { System.out.println("Hi too!"); } }
Friend f1 = new Friend(); Friend f2 = new Friend(); new Thread() { public void run() { f1.greet(f2); } }.start(); new Thread() { public void run() { f2.greet(f1); } }.start();
public class Example { private boolean busy = false; public synchronized boolean isBusy() { return busy; } public synchronized void doHeavyWork() { busy = true; heavyWork(); busy = false; } }
Example e = new Example(); new Thread() { public void run() { while(true) { e.doHeavyWork(); } } }.start(); while(true) { Thread.sleep(1000); System.out.println("busy? " + e.isBusy()); }
public class Person { private boolean left; public void pass(Person p) { while (true) { if (p.canPass(left)) return; else left = !left; Thread.yield(); } } public boolean canPass(boolean other) { if (left == other) { left = !left; return false; } else return true; } }
public class Drop { private boolean empty = true; private String message; public synchronized String take() { while (empty) wait(); empty = true; notifyAll(); return message; } public synchronized void put(String message) { while (!empty) wait(); empty = false; this.message = message; notifyAll(); } }
ExecutorService pool = Executors.newFixedThreadPool(10); while (!pool.isShutdown()) { pool.execute(new Handler(serverSocket.accept())); }
public class MyAction extends RecursiveAction { protected void compute() { int size = list.size(); if (size < 100) { computeDirectly(); } else { invokeAll(new MyAction(list.subList(0, size / 2)), new MyAction(list.subList(size / 2 + 1, size))); } protected void computeDirectly() { // ... } } ForkJoinPool pool = new ForkJoinPool(); pool.invoke(new MyAction(bigList));
ExecutorService pool = Executors.newFixedThreadPool(10); public Future<Document> download(String url) { return pool.submit(new Callable<Document>() { @Override public Document call() { return doDownload(url); } } }
Future<Document> future = download("http://example.com/robots.txt"); future.isDone(); future.cancel(mayInterrupt); future.isCancelled(); Document future.get(timeout); Document future.get();
void example(String ... args); example("one"); example("one", "two");
void example(String[] args);
for (int i = 0; i < list.size(); i++) { String item = list.get(i); // ... }
List list; for (String item : list) { // ... }
Map<Integer, Pair<String, List<Double>>> map = new HashMap<Integer, Pair<String, List<Double>>>()
Map<Integer, Pair<String, List<Double>>> map = new HashMap<>()
List<Person> persons; Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.age - p2.age; } });
Collections.sort(persons, (p1, p2) -> p1.age - p2.age);
public class Person { public static int CompareByAge(Person p1, Person p2) { return p1.age - p2.age; } }
Collections.sort(persons, Person::CompareByAge);
for (Person p : db.getPeople()) { if (p.age > 20) { Address a = p.getAddress(); if (a != null) { System.out.println(a.getCity()); } } }
db.streamPeople() .filter(p -> p > 20) .flatMap(p -> p.getAddress()) .flatMap(Address::getCity) .forEach(System.out::println);