public class OverloadResolution {
    static void show(long x)      { System.out.println("show(long)    " + x); }
    static void show(Integer x)   { System.out.println("show(Integer) " + x); }
    static void show(Object x)    { System.out.println("show(Object)  " + x); }
    static void show(int... x)    { System.out.println("show(int...)  " + x.length + " args"); }

    static void box(Object x)     { System.out.println("box(Object)   " + x); }
    static void box(Integer x)    { System.out.println("box(Integer)  " + x); }
    static void box(int... x)     { System.out.println("box(int...)   " + x.length + " args"); }

    static void pick(double x)    { System.out.println("pick(double)"); }
    static void pick(Object x)    { System.out.println("pick(Object)"); }

    static void nul(Object o)     { System.out.println("nul(Object)"); }
    static void nul(String s)     { System.out.println("nul(String)"); }

    public static void main(String[] args) {
        int i = 5; byte b = 1; char c = 'A'; short s = 2;
        System.out.println("-- phase 1: exact or widening (no boxing) --");
        show(i);
        show(b);
        show(c);
        System.out.println("-- a reference argument matches by subtyping --");
        show(Integer.valueOf(7));
        System.out.println("-- phase 2: boxing (int becomes Integer) --");
        box(5);
        System.out.println("-- phase 3: varargs, last resort --");
        show();
        show(1, 2, 3);
        box();
        System.out.println("-- widening beats boxing: int goes to pick(double), not pick(Object) --");
        pick(i);
        System.out.println("-- most specific wins --");
        nul("text");
        nul(null);
        nul((Object) "text");

        System.out.println("-- overriding: runtime type decides --");
        Animal a = new Dog();
        System.out.println(a.sound());
        System.out.println(a.self().getClass().getSimpleName());
        System.out.println("-- static hiding and field hiding: reference type decides --");
        System.out.println(a.kind() + " / " + ((Animal) new Dog()).name + " / " + new Dog().name);
        System.out.println("-- overload chosen by compile-time type, not runtime --");
        Animal real = new Dog();
        describe(real);
        describe((Dog) real);
    }
    static void describe(Animal a) { System.out.println("describe(Animal)"); }
    static void describe(Dog d)    { System.out.println("describe(Dog)"); }
}
class Animal {
    String name = "animal-field";
    String sound() { return "..."; }
    Animal self() { return this; }
    static String kind() { return "Animal.kind"; }
}
class Dog extends Animal {
    String name = "dog-field";
    @Override String sound() { return "Woof"; }
    @Override Dog self() { return this; }
    static String kind() { return "Dog.kind"; }
}
