Adding to what Kevin said. Here is the most common advantage, in my experience.
Let's say we had the DogInterface, and then two classes which implement the DogInterface: Dog and Puppy.
Declaring a list with the DogInterface type allows us to add both Dog and Puppy objects to the same list. Also, if we are unsure what subclass of a type we will use, we can declare the variable with whatever superclass our options share.
Here is a code example which covers a bunch of different uses for this sort of stuff:
public class Example {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
/* dogs is a list of DogInterface types because it
* can contain BOTH Dog and Puppy objects
*/
List<DogInterface> dogs = new ArrayList<>();
while(true) {
/* rex is of type DogInterface because we do
* not know if the user will create a new
* Dog object or a new Puppy object
*/
DogInterface rex = null;
System.out.println("Enter 'Dog' or 'Puppy'");
String input = in.nextLine();
if(input.equals("Dog")) {
rex = new Dog();
}
else if(input.equals("Puppy")) {
rex = new Puppy();
}
if(rex==null) {
break;
}
dogs.add(rex);
}
for(int i=0;i<dogs.size();i++) {
/* dog is of type DogInterface because we
* do not know if we are getting a Dog or
* a Puppy object from the dogs list
*/
DogInterface dog = dogs.get(i);
/* We use instanceof to determine if the
* DogInterface is "really" a Dog object
* or a Puppy object
*/
if(dog instanceof Dog) {
System.out.println("A dog");
}
else if(dog instanceof Puppy) {
System.out.println("A puppy");
}
}
}
}