14 Replies Latest reply: Aug 21, 2007 5:41 PM by 796254 RSS

    polymorphism

    807605
      question:

      a few years back I wrote a server that used polymorphism via inheritance. It was something like this:

      base class:
      class Player {
      ..
      public void takeTurn() {
      ..
      }
      }

      the child classes:
      class Human extends Player {
      ..
      public void takeTurn() {
      ..
      }
      }

      class Robot extends Player {
      ..
      public void takeTurn() {
      ..
      }
      }

      I realize I can achieve the same result with an interface:
      public interface Player {
      public void takeTurn();
      }


      then the above classes would implement instead of inherit. Which approach should I take? If I am not going to inherit any functionality should I have chosen interfaces instead? They do the same thing.
        • 1. Re: polymorphism
          807605
          thank you guys for responding. I'd like to bring up one more example in regards to this - the collections classes.

          List.java is an interface in which you can interchange ArrayList, Vector, LinkedList or whatever behind in. The author(s) decided to use an interface, why not an abstract class for List.java? Is it true that inheritance is slightly faster than interfaces when it comes to dynamic dispatching? And I would have guessed that there would be some very basic base funtionality. Just wondering.
          • 2. Re: polymorphism
            807605
            sorry, I see now that there is indeed a class called AbstractList. I have to go back now understand why these same methods are defined in the interface..
            • 3. Re: polymorphism
              807605
              sorry, I see now that there is indeed a class called
              AbstractList. I have to go back now understand why
              these same methods are defined in the interface..
              So that if you wanted to make a list that doesn't reuse anything in AbstractList you can simply implement the List interface directly, for one reason.

              This is a fairly common pattern.
              • 4. Re: polymorphism
                807605
                ok, pardon me for having a conversation with myself. I'm going to conjecture why there is both an abstract class and interface attached to ArrayList, Vector et. al. By defining the data type as
                List list = new ArrayList()

                you can swap out this later with another concrete implementation you like better. But if what I read is true, then you are doing this at the expense of slower performance of an interface, if that even makes a difference in the coders application.
                • 5. Re: polymorphism
                  807605
                  what I have always been taught is...

                  A class can only extend one class. So if you want a class to have all the functionality of a super class, then you can simply use the extends. However, if you need to extend multiple super classes ( which isnt allowed in java ) then the best thing to do is to create an interface since you can implement as many interfaces as you want. The only downside is that you have to explicitly override all methods in an interface, which can become tedious.

                  For example, suppose you had several classes that all had some of the same functionality, and therefore had several identical methods. You could extract all of those methods to a super class and then let all of your subclasses extend that class that way you dont have to redefine all of those methods in each subclass.

                  However, if one of your subclasses, lets call it Test, already extends another class, Thread lets say, then you cant have it extend your super class with all of your predefined methods. Here you could create an interface with a list of those methods and just have class Test implement that interface.
                  • 6. Re: polymorphism
                    807605
                    By using both an interface and an abstract class, you can really have the advantages of both. For an ordinary Java program, this double approach may seldom be worthwhile, but for utility classes that all of us use over and over, they found it was.

                    You may even imagine a class implementing List without extending AbstractList (haven't checked if there is one, but you can write one if you want). In other words, the implementor of a list class has the freedom of using AbstractList or not as she likes.
                    • 7. Re: polymorphism
                      807605
                      that makes sense.

                      isn't this slightly better in performance:
                      AbstractList list = new ArrayList();

                      than this:
                      List list = new ArrayList();

                      ?
                      everyone seems to it the latter way
                      • 8. Re: polymorphism
                        796254
                        "slower performance"? What are you talking about?

                        You don't have a very good understanding of interfaces, I fear.

                        %
                        • 9. Re: polymorphism
                          796254
                          that makes sense.

                          isn't this slightly better in performance:
                          AbstractList list = new ArrayList();

                          than this:
                          List list = new ArrayList();

                          ?
                          everyone seems to it the latter way
                          No, just everybody who prefers writing slow, non-performant code.

                          %
                          • 10. Re: polymorphism
                            807605
                            Go ahead and write your own performance tests. Use System.currentTimeMillis(). Do 100 000 or a million operations through either declaration and measure the time it takes. Please report your findings in this thread for the benefit of others.
                            • 11. Re: polymorphism
                              796254
                              Here's one test:
                              package cruft.timing;
                              
                              import java.util.AbstractList;
                              import java.util.ArrayList;
                              import java.util.List;
                              
                              /**
                               * ListTiming
                               * User: Michael
                               * Date: Aug 21, 2007
                               * Time: 10:32:12 AM
                               */
                              public class ListTiming
                              {
                                 private static final int DEFAULT_REPETITIONS = 10000000;
                              
                                 public static void main(String[] args)
                                 {
                                    long begTime = System.currentTimeMillis();
                                    interfaceTime(args);
                                    long endTime = System.currentTimeMillis();
                                    System.out.println("interface time: " + (endTime - begTime));
                              
                                    begTime = System.currentTimeMillis();
                                    abstractTime(args);
                                    endTime = System.currentTimeMillis();
                                    System.out.println("abstract  time: " + (endTime - begTime));
                              
                                 }
                              
                                 private static void abstractTime(String [] args)
                                 {
                                    for (int i = 0; i < DEFAULT_REPETITIONS; ++i)
                                    {
                                       AbstractList<String> strs = new ArrayList<String>(args.length);
                                       for (int j = 0; j < args.length; ++j)
                                       {
                                          strs.add(args[j]);
                                       }
                                    }
                                 }
                              
                              
                                 private static void interfaceTime(String [] args)
                                 {
                                    for (int i = 0; i < DEFAULT_REPETITIONS; ++i)
                                    {
                                       List<String> strs = new ArrayList<String>(args.length);
                                       for (int j = 0; j < args.length; ++j)
                                       {
                                          strs.add(args[j]);
                                       }
                                    }
                                 }
                              
                              
                              }
                              And the results on my Windows machine:

                              interface time: 1687
                              abstract time: 1672

                              Looks like abstract is slightly more performant after all. My apologies.

                              But I'd still prefer the interface over the abstract type. It's not about performance.

                              %
                              • 12. Re: polymorphism
                                807605
                                I saw no real difference between the two from my test by adding an element via either List or AbstractList. So I added half a million object to 3 different types and averaged it over 25 iterations. Feel free to critique my approach:
                                package test;
                                
                                import java.util.*;
                                
                                public class CollectionsTest {
                                     
                                
                                     public static void main(String args[]) {
                                          new CollectionsTest().start();
                                     }
                                     
                                     /**
                                      * throw out first iteration from the average
                                      * because it often falls far outside the standard deviation
                                      */
                                     private void start() {
                                          
                                          System.out.println("start test");
                                          
                                          test1();
                                          test2();
                                          
                                     }
                                     
                                     private void test1() {
                                          int capacity = 500000;
                                          int totalIterations = 25;
                                          long sum = 0;
                                          
                                          System.out.println("--- abstract class approach ---");
                                          for(int j = 0; j < totalIterations; j++) {
                                               long startTime = System.currentTimeMillis();
                                               AbstractList<Integer> list1 = new ArrayList<Integer>(capacity);
                                               AbstractList<Integer> list2 = new Vector<Integer>(capacity);
                                               AbstractList<Integer> list3 = new LinkedList<Integer>();
                                               
                                               for(int i = 0; i < capacity; i++) {
                                                    addInteger(list1, i);
                                                    addInteger(list2, i);
                                                    addInteger(list3, i);
                                               }
                                               long delta = System.currentTimeMillis() - startTime;
                                               
                                               System.out.print(delta + " ");
                                               
                                               if(j == 0) continue;
                                               sum += delta;
                                               
                                          }
                                          long average = sum/(long)totalIterations;
                                          System.out.println("\nthe average time is " + average);
                                          System.out.println("\n");
                                          
                                     }
                                     
                                     private void test2() {
                                          int capacity = 500000;
                                          int totalIterations = 25;
                                          long sum = 0;
                                          
                                          System.out.println("--- interface approach ---");
                                          for(int j = 0; j < totalIterations; j++) {
                                               long startTime = System.currentTimeMillis();
                                               List<Integer> list1 = new ArrayList<Integer>(capacity);
                                               List<Integer> list2 = new Vector<Integer>(capacity);
                                               List<Integer> list3 = new LinkedList<Integer>();
                                               
                                               for(int i = 0; i < capacity; i++) {
                                                    addInteger(list1, i);
                                                    addInteger(list2, i);
                                                    addInteger(list3, i);
                                               }
                                               long delta = System.currentTimeMillis() - startTime;
                                               
                                               System.out.print(delta + " ");
                                               
                                               if(j == 0) continue;
                                               sum += delta;
                                          }
                                          long average = sum/(long)totalIterations;
                                          System.out.println("\nthe average time is " + average);
                                          System.out.println("\n");
                                          
                                     }
                                     
                                     private void addInteger(List<Integer> list, int i) {
                                          list.add(new Integer(i));
                                     }
                                     
                                     
                                     private void addInteger(AbstractList<Integer> list, int i) {
                                          list.add(new Integer(i));
                                     }
                                     
                                     
                                     
                                }
                                my results:

                                --- abstract class approach ---
                                1000 610 719 890 656 594 562 750 532 562 563 843 516 719 734 656 672 781 797 766 672 640 641 641 609
                                the average time is 645


                                --- interface approach ---
                                641 609 609 610 625 656 641 625 656 656 703 750 782 781 765 797 782 781 640 532 531 531 531 532 531
                                the average time is 626
                                • 13. Re: polymorphism
                                  807605
                                  I did some runs of your benchmark on my machine, and "abstract approach" was always faster. Comparability and significance of these tests aside, one can generalize this abstract vs interface to virtual vs interface here, because abstract methods are just a kind of virtual methods. And by design, interface invocations can never be faster than virtual method invocations, because a call to an interface method involves some extra work (which might be optimized to a certain extent, though, I don't know): while invokevirtual knows about the actual target class at link time, invokevinterface does not. The target is only known immediately at invocation time, and the dispatch code has to determine the virtual method to be invoked by some lookup procedure first. This paper (from 2001) gives a (now obsolete?) overview of the topic:
                                  Efficient Implementation of Java Interfaces: Invokeinterface Considered Harmless
                                  http://citeseer.ist.psu.edu/alpern01efficient.html

                                  And whether I'd use
                                  AbstractList list = new ArrayList();
                                  // or
                                  List list = new ArrayList();
                                  depends on scope. If it's small (e.g. local variable), I just use
                                  ArrayList list = new ArrayList();
                                  an interface List (or even the abstract base) doesn't have a benefit here, to me. Otherwise (larger scope, fields, params etc.) and if available, I tend to use interfaces. You get all the flexibility, and the performance loss... I suppose is really negligible for most practical purposes.
                                  • 14. Re: polymorphism
                                    796254
                                    I did some runs of your benchmark on my machine, and
                                    "abstract approach" was always faster.
                                    Yes, but we're talking millisecond differences for ten million repetitions in my case.
                                    Comparability
                                    and significance of these tests aside, one can
                                    generalize this abstract vs interface to virtual vs
                                    interface here, because abstract methods are just a
                                    kind of virtual methods. And by design, interface
                                    invocations can never be faster than virtual method
                                    invocations, because a call to an interface method
                                    involves some extra work (which might be optimized to
                                    a certain extent, though, I don't know): while
                                    invokevirtual knows about the actual target class at
                                    link time, invokevinterface does not. The target is
                                    only known immediately at invocation time, and the
                                    dispatch code has to determine the virtual
                                    method to be invoked by some lookup procedure first.
                                    This paper (from 2001) gives a (now obsolete?)
                                    overview of the topic:
                                    Efficient Implementation of Java Interfaces:
                                    Invokeinterface Considered Harmless
                                    http://citeseer.ist.psu.edu/alpern01efficient.html

                                    And whether I'd use
                                    AbstractList list = new ArrayList();
                                    // or
                                    List list = new ArrayList();
                                    depends on scope.
                                    For me, it also depends on the likelihood that I'd want to change the implementation with minimal impact.

                                    %