java: relationship of the Runnable and Thread interfaces
I realize that the method run()
must be declared because its declared in the Runnable
interface. But my question comes when this class runs how is the Thread
object allowed if there is no import call to a particular pac开发者_如何转开发kage? how does runnable know anything about Thread or its methods? does the Runnable interface extend the Thread class? Obviously I don't understand interfaces very well. thanks in advance.
class PrimeFinder implements Runnable{
public long target;
public long prime;
public boolean finished = false;
public Thread runner;
PrimeFinder(long inTarget){
target = inTarget;
if(runner == null){
runner = new Thread(this);
runner.start()
}
}
public void run(){
}
}
In this situation, I like to think of interfaces as contracts. By saying that your class implements Runnable, you are explicitly stating that your class adheres to the Runnable contract. This means that other code can create an instance of your class and assign to a Runnable type:
Runnable r = new PrimeFinder();
Further, by adhering to this contract you are guaranteeing that other code calling your class can expect to find the methods of Runnable implemented (in this case run() ).
Nope. Thread is in lava.lang package, so it's implicity imported.
And: Thread knows Runnable.
That's why Thread receives an Runnable (this
implements Runnable) and calls its method run() inside its own thread of execution.
The Thread mantains a reference to the Runnable you implement:
public Thread(Runnable runnable) {
this.myRunnable = runnable;
}
private Runnable myRunnable;
The start method of Thread class could look like:
public void start() {
// do weird stuff to create my own execution and...
myRunnable.run();
}
Yes, Thread
implements Runnable
.
As the API references states runnable interface is designed to provide a common protocol for objects that wish to execute code while they are active.
You are getting confused because there are two ways of making this kind of concurrency in Java:
- you can extend a
Thread
class overriding the defaultrun
method, then invoke the thread in a way similar tonew MyThread().start()
- you can write a class that implements the
Runnable
interface and start it in a similar way:new Thread(new MyRunnable()).start()
These approaches are IDENTICAL. Infact the run
method of class Thread
normally calls the run
method of Runnable
object attached to the thread if any, otherwise it returns.
What is the need of having a Runnable
interface? It's useful because it declares a protocol to let classes be considered with specific characteristics.
This is the same thing of Comparable
interface or Serializable
interface, but here you effectively have a method to override (public void run()
) while for example Serializable
is just a trait you give to your class.
A final example is the fact that TimerTask
implements Runnable
. TimerTask
is used together with Timer
class to execute delayed or periodic tasks, so it makes sense that a timertask is also runnable, so that Timer can launch tasks using exactly that method.
EDIT: since you seem confused by the usefulness of an interface you have to think that: Java is a statically typed language. What does it mean? It means that it needs to know everything about a type during compilation to be able to guarantee that not run-time type error will ever be thrown.
Ok, now suppose that Java API supports a hipotetically class to draw shapes. So you can write your own classes for the shapes and then feed them to this class (let's call it ShapeDrawer
).
ShapeDrawer
needs to know how to draw the shapes you pass to it and the only way to be sure of it is to decide that every Shape
object must have a method called public void drawMe()
, so that ShapeDrawer
can call this method on every Shape
you attach to it without knowing anything more than this.
So you declare an interface
public interface Shape
{
public void drawMe();
}
that classes can use to be considered a Shape
. And if a class is a Shape
you can pass it to your ShapeDrawer
class with no problem:
class ShapeDrawer
{
public void addShape(Shape shape) { ... }
public void drawShapes()
{
for (Shape s : shapes)
s.drawMe();
}
}
So that compiler is happy because when adding shapes you add classes that implements Shape
, you class knows exactly how to draw such shapes and developers are happy because you separated the common protocol of an object from their specific implementations.
It's a sort of contract, if you want a Triangle
class that is able to be drawn by ShapeManager
you have to the declare that method, so that you can call for example
shapeDrawerInstance.addShape(new Triangle())
This has nothing to do with interfaces. Rather, Thread is in the java.lang package and since java.lang is the default package, you don't need to import it. That is, java.lang.* is imported by default, so you don't need to explicitly import it yourself.
The class Thread implements Runnable. Runnable does not know anything about Thread, but Thread knows everything about Runnable.
Other classes that use Thread know that Thread implements the interface specified in Runnable.
Your code the way it's written doesn't compile. You declared PrimeFinder implements Runnable
, but it doesn't actually @Override public void run()
.
As for why interfaces are useful, it's because they define types that you can work with regardless of implementations. If you work with a Closeable
, for example, you know you can close()
it. The actual class may be any of the many that implements it (see full list), but working with the interface allows you to step back and just say that they're all Closeable
.
With interfaces, you don't inherit implementation; you inherit a TYPE. If there's another code that works with Closeable
types (for example, all it does is invoke close()
on them with exception checking), then you can pass it anything that implements Closeable
. In fact, this "please close this thing for me, okay thanks" utility method already exists, and it's very flexible and highly reusable since it works with the interface.
By using interfaces instead of specific implementation classes, you encapsulate your logic much more cleanly. An algorithm that works with a Set
interface, for example, doesn't care if it's actually a TreeSet
or a HashSet
. This lack of dependency is a good thing, because it allows the algorithm to be very flexible -- it can also work with a SuperMagicalSet implements Set
in the future, for example.
Working with interfaces also ensures proper encapsulation, because not knowing what the actual implementation class will be, you must work only with what the interface provides.
I recommend reading Josh Bloch's Effective Java 2nd Edition:
- Item 18: Prefer interfaces to abstract classes
- Item 19: Use interfaces to define types
精彩评论