1

Let's say you have some Java code as follows:

 public class Base{
    public void m(int x){
    // code
    }
 }

and then a subclass Derived, which extends Base as follows:

 public class Derived extends Base{
    public void m(int x){ //this is overriding
      // code
    }

    public void m(double x){ //this is overloading
      // code
    }
 }

and then you have some declarations as follows:

Base b = new Base();
Base d = new Derived();
Derived e = new Derived();

b.m(5); //works
d.m(6); //works
d.m(7.0); //does not compile
e.m(8.0); //works

For the one that does not compile, I understand that you are passing in a double into Base's version of the m method, but what I do not understand is... what is the point of ever having a declaration like "Base b = new Derived();" ?

It seems like a good way to run into all kinds of casting problems, and if you want to use a Derived object, why not just go for a declaration like for "e"?

Also, I'm a bit confused as to the meaning of the word "type" as it is used in Java. The way I learned it earlier this summer was, every object has one class, which corresponds to the name of the class following "new" when you instantiate an object, but an object can have as many types as it wants. For example, "e" has type Base, Derived, (and Object ;) ) but its class is Derived. Is this correct?

Also, if Derived implemented an interface called CanDoMath (while still extending Base), is it correct to say that it has type "CanDoMath" as well as Base, Derived, and Object?

4

4 に答える 4

4

I often write functions in the following form:

public Collection<MyObject> foo()  {}  

public void bar(Collection<MyObject> stuff){}

I could just as easily have made it ArrayList in both instances, however what happens if I later decide to make the representation a Set? The answer is I have a lot of refactoring to do since I changed my method contract. However, if I leave it as Collection I can seamlessly change from ArrayList to HashSet at will. Using the example of ArrayList it has the following types:

 Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
于 2012-08-03T00:58:11.590 に答える
3

There are a number of cases where confining yourself to a particular (sub)class is not desired, such as the case you have where e.m(8.0);. Suppose, for example, you have a method called move that moves an object in the coordinate graph of a program. However, at the time you write the method you may have both cartesian and radial graphs, handled by different classes.

If you rely on knowing what the sub-class is, you force yourself into a position wherein higher levels of code must know about lower levels of code, when really they just want to rely on the fact that a particular method with a particular signature exists. There are lots of good examples:

  • Wanting to apply a query to a database while being agnostic to how the connection is made.
  • Wanting to authenticate a user, without having to know ahead of time the strategy being used.
  • Wanting to encrypt information, without needing to rip out a bunch of code when a better encryption technique comes along.

In these situations, you simply want to ensure the object has a particular type, which guarantees that particular method signatures are available. In this way your example is contrived; you're asking why not just use a class that has a method wherein a double is the signature's parameter, instead of a class where that isn't available. (Simply put; you can't use a class that doesn't have the available method.)

There is another reason as well. Consider:

class Base {
   public void Blah() {
     //code
   }
}

class Extended extends Base {
   private int SuperSensitiveVariable;

   public setSuperSensistiveVariable(int value) {
     this.SuperSensistiveVariable = value;
   }

   public void Blah() {
     //code
   }
}

//elsewhere
Base b = new Extended();
Extended e = new Extended();

Note that in the b case, I do not have access to the method set() and thus can't muck up the super sensitive variable accidentally. I can only do that in the e case. This helps make sure those things are only done in the right place.

Your definition of type is good, as is your understanding of what types a particular object would have.

于 2012-08-03T01:00:56.340 に答える
2

持っていることのポイントは何Base b = new Derived();ですか?

これのポイントは、ポリモーフィズムを使用して実装を変更することです。たとえば、誰かが次のことを行う可能性があります。

List<String> strings = new LinkedList<String>();

彼らがいくつかのプロファイリングを行い、このリストで最も一般的な操作がリストのタイプに対して非効率的であることがわかった場合、彼らはそれをArrayListと交換することができます。このようにして、柔軟性が得られます。

派生オブジェクトを使用する場合

派生オブジェクトのメソッドが必要な場合は、派生オブジェクトを使用します。BufferedInputStreamクラスを見てください。これは、内部実装のためではなく、InputStreamをラップし、便利なメソッドを提供するために使用します。

また、Javaで使用されている「タイプ」という言葉の意味についても少し混乱しています。

先生がインターフェースとクラスを「タイプ」と呼んでいるようです。インターフェイスを実装し、クラスを拡張するクラスは3つの方法で参照できるため、これは合理的な抽象化です。

public class Foo extends AbstractFoo implements Comparable<Foo>

// Usage
Comparable<Foo> comparable = new Foo();
AbstractFoo abstractFoo = new Foo();
Foo foo = new Foo();

さまざまなコンテキストで使用されているタイプの例:

new ArrayList<Comparable>().Add(new Foo()); // Foo can be in a collection of Comparable
new ArrayList<AbstractFoo>().Add(new Foo()); // Also in an AbstractFoo collection
于 2012-08-03T01:19:40.457 に答える
1

This is one of the classic problems on object oriented designs. When something like this happens, it usually means the design can be improved; there is almost always a somewhat elegant solution to these problems....

For example, why dont you pull the m that takes a double up into the base class?

With respect to your second question, an object can have more than one type, because Interfaces are also types, and classes can implement more than one interface.

于 2012-08-03T00:49:09.570 に答える