Skip to content

Instantly share code, notes, and snippets.

@edubkendo
Last active December 30, 2015 04:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edubkendo/7779568 to your computer and use it in GitHub Desktop.
Save edubkendo/7779568 to your computer and use it in GitHub Desktop.
An explanation of Java Interfaces via Ruby's Ducktyping.

##Interfaces

Duck typing

So in ruby, we may have some method that takes an parameter duck, and calls some method, quack on that parameter.

class Duck
  def quack
    puts "Quack"
  end
end

def duck_type(duck)
  duck.quack
end

my_duck = Duck.new  # => #<Duck:0x007f95b9824fa0>
duck_type(my_duck)

# >> Quack

But, we can pass anything we want into that method, and as long as it has a method defined on it called quack, the code will run just fine. This is what we mean by duck typing.

class Duck
  def quack
    puts "Quack"
  end
end

class Chicken
  def quack
    puts "Ummm, cluck, cluck, cluck"
  end
end

def duck_type(duck)
  duck.quack
end

my_duck = Duck.new  # => #<Duck:0x007f91d46c4310>

duck_type(my_duck)

my_chick = Chicken.new  # => #<Chicken:0x007f91d46cff30>

duck_type(my_chick)

# >> Quack
# >> Ummm, cluck, cluck, cluck

Static Typing - Inheritance

But, this is a lot harder to accomplish in a statically typed language like java. Java needs to know what type of object is being passed into and returned from any given method, but sometimes we may have a method which needs to be able to take two different types of objects. One way to handle this is inheritance.

public class DuckTyping {

    public static void duckTyping(Duck duck) {
        duck.quack();
    }
}

public class Duck {
    public void quack() {
        System.out.println("Quack!");
    }
}

public class Chicken extends Duck {

    @Override
    public void quack() {
        System.out.println("Um, cluck, cluck!");
    }
}

public class Main {

    public static void main(String[] args) {
        Duck myDuck = new Duck();
        DuckTyping.duckTyping(myDuck);

        Chicken myChicken = new Chicken();
        DuckTyping.duckTyping(myChicken);
    }

}

// =>   Quack!
// =>   Um, cluck, cluck!

Static Typing - Interfaces

But what if our Chicken already inherits from something else? This is where interfaces come in.

public interface DuckType {
    public void quack();
}

public class Duck implements DuckType {

    // The Duck class has to implement all methods from any interface it
    // implements. This way, the compiler knows what methods it can call on
    // anything accepting a "DuckType" parameter.

    @Override
    public void quack() {
        System.out.println("Quack!");
    }
}

public class ChickenPrototype {
    public void somethingImportantAndSpecificToChickens() {
        System.out.println("Yo, Chickens have to do this thing.");
    }
}

public class Chicken extends ChickenPrototype implements DuckType {

    // The Chicken class BOTH inherits all the methods from ChickenPrototype
    // And implements the methods in the DuckType interface. So it can be
    // passed in as an argument to any method that accepts any of Chicken,
    // DuckType or ChickenPrototype as a parameter.

    @Override
    public void quack() {
        System.out.println("Um, cluck, cluck!");
    }
}

public class DuckTyping {

    // Notice we are now accepting "DuckType" as the param here, instead
    // of "Duck".

    public static void duckTyping(DuckType duck) {
        duck.quack();
    }
}

public class Main {

    public static void main(String[] args) {
        Duck myDuck = new Duck();
        DuckTyping.duckTyping(myDuck);

        Chicken myChicken = new Chicken();
        DuckTyping.duckTyping(myChicken);
        myChicken.somethingImportantAndSpecificToChickens();
    }

}

// =>   Quack!
// =>   Um, cluck, cluck!
// =>   Yo, Chickens have to do this thing.

Anonymous Inner Classes

Where this is especially useful is when you have something like a listener, where the class using it will be likely to be writing a small amount of code on the fly that then needs to be run. We can demonstrate this easily with a tiny addition to our main class:

public class Main {

    public static void main(String[] args) {
        Duck myDuck = new Duck();
        DuckTyping.duckTyping(myDuck);

        Chicken myChicken = new Chicken();
        DuckTyping.duckTyping(myChicken);
        myChicken.somethingImportantAndSpecificToChickens();

        DuckTyping.duckTyping(new DuckType() {
            @Override
            public void quack() {
                System.out.println("Wassup dawg. This ain't your usual quack.");
            }
        });
    }

}

// =>   Quack!
// =>   Um, cluck, cluck!
// =>   Yo, Chickens have to do this thing.
// => Wassup dawg. This ain't your usual quack.

We instantiate an Anonymous Inner Class (unnamed class written directly inside another class) that implements our DuckType interface right there, and pass it directly into DuckTyping#duckTyping instead of passing it a Duck or Chicken object.

In dynamic languages like ruby, we can figure all these types and method lookups at runtime, so we can have things like real duck-typing without all this rigamarole. But there's a tradeoff in terms of speed and typesafety. In java or other statically typed languages, we need something like interfaces to handle these kinds of situations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment