質問

ExecutorService and Service are interfaces and so only have abstract methods, which means that their methods are not implemented. How then can we call, e.g., future.get(), es.submit(), and es.shutdown() methods on references of the interface type? E.g., why can we do the following?

Future f = ...
f.get();

Here's a more concrete example:

import java.util.concurrent.*;
class Factorial implements Callable<Long> {
    long n;
    public Factorial(long n) {
        this.n = n;
    }

    public Long call() throws Exception {
        if (n <= 0) {
            throw new Exception("for finding factorial, N should be > 0");
        }
        long fact = 1;
        for(long longVal = 1; longVal <= n; longVal++) {
            fact *= longVal;
        }
        return fact;
    }
}

class CallableTest {
    public static void main(String []args) throws Exception {
        long N = 20;

        Callable<Long> task = new Factorial(N);
        ExecutorService es = Executors.newSingleThreadExecutor();
        Future<Long> future = es.submit(task);

        System.out.printf("factorial of %d is %d", N, future.get());
        es.shutdown();
    }
}
役に立ちましたか?

解決

This question got some downvotes because in one sense it's sort of basic, but I think it's actually sort of interesting. After all, a factory class like Executors in your example, can specify that it returns Executors. But, as you point out, that's just an interface, and you'd actually need an instance of something that implements Executor if you're going to be able to call those methods. Why doesn't the factory have to tell us the actual type of thing that's being returned?

If you haven't seen this before, it might not be clear how you could do such a thing. The important thing to remember is that if you have a class that implements an interface, then a method declared to return the interface type can return an instance of the class. That is, you can do:

interface Foo { /* ... */ }

class Bar implements Foo { /* ... */ }

class Factory {
  Foo makeFoo() { 
    return new Bar( /*... */ );
  }
}

makeFoo is declared to return a Foo, but Foo is an interface; you can't actually have instance of it. You can only have instance of classes that implement Foo. Bar does implement Foo, so you can return an instance of Bar.

The reason that we can do this is because when it comes time to invoke a method on an object, the implementation of the method is found in the actual object that we have a reference to. The way that methods are looked up is actually a bit complicated. Conceptually though, you might think of it like this: if you tell me that you're a Foo, then I can ask you to run any of the methods that are declared in Foo, but you get to decide exactly what you do for that method. I only get to use your Foo type in determining what methods I can ask you to execute. This is very important, and is why we can override methods in subclasses. These are called virtual methods. One of the reasons this is so important is that it enables us to use interfaces where we can reveal a minimal amount of information about our implementation (we can choose to say "I implement Foo, but that's all I'm telling you about myself), but still abide by a contract (i.e., I'm guaranteed to actually implement all the methods declared in Foo).

The following example is a bit more in depth, and captures a bit more of the factory pattern that you see with Executors.

Code

public class InterfacesExample {

    /**
     * An interface with one method.
     */
    interface Frobber {
        /**
         * Frob the object.
         * @param object the object
         */
        void frob( Object object );
    }

    /**
     * A factory class with one method for creating printing frobber.
     */
    public static class Frobbers {
        /**
         * Returns a Frobber whose {@link Frobber#frob(Object)} method
         * prints its argument
         * @return a printing Frobber
         */
        public static Frobber newPrintingFrobber() {
            // This returns an instance of an anonymous class
            // that implements the Frobber interface.  It has 
            // to provide an implementation of frob(Object),
            // though.
            return new Frobber() {
                @Override
                public void frob( final Object object ) {
                    System.out.println( "Frobbing "+object+"..." );
                }
            };
        }

        /**
         * Returns a Frobber whose {@link Frobber#frob(Object)} method
         * prints the prefix and its argument
         * @param prefix an object
         * @return a prefixing printing Frobber
         */
        public static Frobber newPrefixingPrintingFrobber( final Object prefix ) {
            return new PrefixingPrintingFrobber( prefix );
        }

        /**
         * A private, but not anonymous class.  Instances shouldn't be
         * made with its constructor, but rather through the factory 
         * method {@link Frobbers#newPrefixingPrintingFrobber(Object)}. 
         */
        private static class PrefixingPrintingFrobber implements Frobber {
            final Object prefix;

            public PrefixingPrintingFrobber( Object prefix ) { 
                this.prefix = prefix;
            }

            @Override
            public void frob( final Object object ) {
                System.out.println( "Frobbing "+prefix+":"+object+"..." );
            } 

        }
    }

    /**
     * Create some frobbers with the factory and test them out.
     */
    public static void main( final String[] args ) {
        final Frobber f1 = Frobbers.newPrintingFrobber();
        f1.frob( 42 );

        final Frobber f2 = Frobbers.newPrefixingPrintingFrobber( "boing" );
        f2.frob( 36 );
    }
}

Output

Frobbing 42...
Frobbing boing:36...
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top