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);