Polymorphism
class Super{
public int field=0;
public int getField(){
return field;
}
}
class Sub extends Super{
public int field=1;
public int getField(){
return field;
}
public int getSuperField(){
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup=new Sub();
System.out.println("sup.field = "+sup.field+", sup.getField() = "+sup.getField());
Sub sub=new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = "+sub.getField()+", sub.getSuperField() = "+sub.getSuperField());
}
}
Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
When a sub object is upcast to a Super reference, any field accesses are resolved by the compiler, and are thus not polymorphic. In this example, difference storage is allocated for Super.field and Sub.field. Thus, Sub actually contains two fields called field: its own and the one that it gets from Super. However, the Super version is not the default that is produced when you refer to field in Sub; in order to get the Super field you must explicityly say super.field.
Although this seems like it could be a confusing issue, in practice it virtually never comes up. For one thing, you’ll generally make all fields private and so you won’t access them directly, but only as side effects of calling methods. In addition, you probably won’t give the same name to a base-class field and a derived-class field, because it is confusing.
class StaticSuper{
public static String staticGet(){
return "Base staticGet()";
}
public String dynamicGet(){
return "Base dynamicGet()";
}
}
class StaticSub extends StaticSuper{
public static String staticGet(){
return "Derived staticGet()";
}
public String dynamicGet(){
return "Derived dynamicGet()";
}
}
public class StaticPolymorphism {
public static void main(String[] args) {
StaticSuper sup=new StaticSub();
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
Output:
Base staticGet()
Derived dynamicGet()
static methods are associated with the class,and not the individual objects.
class Glyph{
void draw(){
System.out.println("Glyph.draw()");
}
Glyph(){
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph{
private int radius=1;
RoundGlyph(int r){
radius=r;
System.out.println("RoundGlyph.RoundGlyph(), radius = "+radius);
}
void draw(){
System.out.println("RoundGlyph.draw(), radius = "+radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
Glyph.draw() is designed to be overridden, which happens in RoundGlyph. But the Glyph constructor calls this method, and the call ends up in RoundGlyph.draw(), which would seem to be the intent. But if you look at the output, you can see that when Glyph’s constructor calls draw(), the value radius isn’t even the default initial value 1. It’s 0.
A good guideline for constructors is “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any other methods in this class.” The only safe methods to call inside a constructor are those that are final in the base class. (This also applies to private methods, which are automatically final.) These cannot be overridden and thus cannot produce this kind of surprise. You may not always be able to follow this guideline, but it’s something to strive towards.
Once you learn about polymorphism, it can seem that everything ought to be inherited, because polymorphism is such a clever tool. This can burden your designs; in fact, if you choose inheritance first when you’re using an existing class to make a new class, things can become needlessly complicated.
A better approach is to choose composition first, especially when it’s not obvious which one you should use. Composition does not force a design into an inheritance hierarchy. But composition is also more flexible since it’s possible to dynamically choose a type (and thus behavior) when using composition, whereas inheritance requires that an exact type be known at compile time.
Interfaces
interface CanFight{
void fight();
}
interface CanSwim{
void swim();
}
interface CanFly{
void fly();
}
class ActionCharacter{
public void fight(){}
}
class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{
public void swim(){}
public void fly(){}
}
public class Adventure {
public static void t(CanFight x){x.fight();}
public static void u(CanSwim x){x.swim();}
public static void v(CanFly x){x.fly();}
public static void w(ActionCharacter x){x.fight();}
public static void main(String[] args) {
Hero h=new Hero();
t(h);
u(h);
v(h);
w(h);
}
}
interface CanFight{
void fight();
}
interface CanSwim{
void swim();
}
interface CanFly{
void fly();
}
class ActionCharacter{
public void fight(){}
}
class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{
public void swim(){}
public void fly(){}
}
public class Adventure {
public static void t(CanFight x){x.fight();}
public static void u(CanSwim x){x.swim();}
public static void v(CanFly x){x.fly();}
public static void w(ActionCharacter x){x.fight();}
public static void main(String[] args) {
Hero h=new Hero();
t(h);
u(h);
v(h);
w(h);
}
}
Keep in mind that one of the core reasons for interfaces is shown in the preceding example: to upcast to more than one base type (and the flexibility that this provides). However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface.
Interfaces may be nested within classes and within other interfaces. This reveals a number of interesting features:
class A{
interface B{
void f();
}
public class BImp implements B{
public void f(){}
}
private class BImpl2 implements B{
public void f(){}
}
public interface C{
void f();
}
class CImp implements C{
public void f(){}
}
private class CImp2 implements C{
public void f(){}
}
private interface D{
void f();
}
private class DImp implements D{
public void f(){}
}
public class DImp2 implements D{
public void f(){}
}
public D getD(){return new DImp2();}
private D dRef;
public void receiveD(D d){
dRef=d;
dRef.f();
}
}
interface E{
interface G{
void f();
}
//Redundant "public";
public interface H{
void f();
}
void g();
// Cannot be private within an interface:
//! private interface I{}
}
public class NestingInterfaces {
public class BImp implements A.B{
public void f(){}
}
class CImp implements A.C{
public void f(){}
}
//Cannot implement a private interface except
//within that interface's defining class:
// class DImp implements A.D{
// public void f(){}
// }
class EImp implements E{
public void g(){}
}
class EGImp implements E.G{
public void f(){}
}
class EImp2 implements E{
public void g(){}
class EG implements E.G{
public void f(){}
}
}
public static void main(String[] args) {
A a=new A();
// Can't access A.D
// A.D ad=a.getD();
// Doesn't return anyting but A.D
// A.DImp2 di2=a.getD();
// Cannot access a member of the interface:
// a.getD().f();
// Only another A can do anyting with getD():
A a2=new A();
a2.receiveD(a.getD());
}
}
Implementing a private interface is a way to force the definition of the methods in that interface without adding any type information (that is, without allowing any upcasting).
When you implement an interface, you are not required to implement any interfaces nested within. Also, private interfaces cannot be implemented outside of their defining classes.
Inner Classes
public class Parcel {
static class Contents{
private int i=11;
public int value(){return i;}
}
class Destination{
private String label;
Destination(String whereTo){label=whereTo;}
String readLabel(){return label;}
}
public void test(){
Destination d=new Destination("");
}
public static void main(String[] args) {
Contents c=new Contents();
Parcel p=new Parcel();
Parcel.Destination d=p.new Destination("");
}
}
If you want to make an object of the inner class anywhere except from within a non-static method of the outer class, you must specify the type of that object as OuterClassName.InnerClassName, as seen in main().
It’s not possible to create an object of the inner class unless you already have an object of the outer class. This is because the object of the inner class is quietly connected to the object of the outer class that it was made from. However, if you make a nested class (a static inner class), then it doesn’t need a reference to the outer-class object.