views:

554

answers:

1

I'm having trouble with inheritance. I can do what I need with single instances of the parent class and child class - as expected the methods of the parent class work fine with instances of the child class.

But, I cannot see how to extend a class that uses an array of the parent class, in a way that allow me to store an array of the child class. Sorry about the size of the code, but I don't think I can make the issue clear with a smaller snippet.

Is there a neat way out of this problem?

public class TestIn {
    public static void main(String args[])  {
        CPUStats c = new CPUStats();
        c.set(4,5);  // OK
        c.dump();
        CPUStatsArray ca = new CPUStatsArray(24);
        ca.set(3, 21, 25);  // Data for hour 3 = 21 and 25
        ca.dump();              // Fails
    }
}

class GenericStats  {
    long s[];            // The stats, e.g. [0]=CPU% and [1]=Mem%
    // snip, more members

    // Constructor setting statistics to all zeros
    public GenericStats(int n)  {
        s = new long[n];
    }

    void set(long ... v)  {
        s = new long[v.length];
        for (int i = 0 ; i < v.length ; i++)  
         s[i] = v[i];
    }

    void dump()  {
        for (int i = 0 ; i < s.length ; i++)
            System.out.print("  [" + i + "] = " + s[i]);
        System.out.println();
    }
    // snip, a few useful methods
}

class CPUStats  extends GenericStats  {
    public CPUStats()  {
        super(2);
    }
    void dump() {
        System.out.println("CPU% = " + s[0] + ", and Mem% = " + s[1]);
    }
}

// class WhateverStats extends GenericStats  {
// ....
// }

// So far so good, I can use the useful methods of GenericStats on a CPUStats object

// Now I want an array of sets of statistics, e.g. for 24 sets of stats, one per hour 
class GenericStatsArray  {
   GenericStats gs[];
   int gslen;    // Length of s in each gs[]  (not the length of gs[])

    public GenericStatsArray(int numSets, int inlen)  {
        gs = new GenericStats[numSets];    // Problem caused by using the base class here
        gslen = inlen;
    }

    // Set values for element (e.g. hour) n
    void set(int n, long ... v)  {
        if (gs[n] == null)
         gs[n] = new GenericStats(gslen);
        gs[n].s = new long[v.length];
        for (int i = 0 ; i < v.length ; i++)  
         gs[n].s[i] = v[i];
    }

    void dump()  {
        System.out.println("GenericStatsArray");
        for (int i = 0 ; i < gs.length ; i++)  
         if (gs[i] != null)  {
          System.out.print("Array element [" + i + "]  ");
          gs[i].dump();
         }
        System.out.println("End GenericStatsArray\n");
    }
}

class CPUStatsArray extends GenericStatsArray  {
    public CPUStatsArray(int numSets)  {
        super(numSets, 2);
    }
 // Set values for element (e.g. hour) n
    void set(int n, long ... v)  {
        assert (v.length == 2);
        super.set(n, v);
    }
    void dump()  {
        System.out.println("CPUStatsArray");
        for (int i = 0 ; i < gs.length ; i++)  
         if (gs[i] != null)  {
          CPUStats c = (CPUStats) gs[i];  // This fails, 'GenericStats cannot be cast to CPUStats'
          System.out.print("Array element [" + i + "]  ");
          c.dump();
         }
        System.out.println("End CPUStatsArray\n");
    }
}
+2  A: 

You should use Generics for your GenericsStatsArray:

public class GenericStatsArray<T extends GenericStats> {
  GenericStats[] gs;
  int gslen;

  public GenericStatsArray(int numSets, int inlen)  {
    gs = new GenericStats[numSets];
    gslen = inlen;
  }

  //...
}

You will probably need an abstract factory-method in your class, since you cannot instantiate a generic element (i.e. you cannot say new T()).

Do something like this:

public abstract class GenericStatsArray<T extends GenericStats> {
 // ...
  protected abstract T NewT(int gslen);
  // ...
    gs[n] = NewT(gslen); // was: gs[n] = new GenericStats(gslen);
}

public class CPUStatsArray extends GenericStatsArray<CPUStats> {
  protected CPUStats NewT(int gslen){
    return new CPUStats(gslen);
  }
}
Rasmus Faber
Thanks! I even accidentally used the correct word (generic).
How do you propose to create the array "gs"? I guess the child class will have to do it, because it knows the actual type of T. The parent class can't, because it doesn't know T at runtime. P.S. In your CPUStatsArray, the "T" should probably be "CPUStats"
newacct
@newacct: You are right. I have been writing mostly C#-code lately, so I had forgotten about type-erasure. You could let the child class do it, but you could also just use an array of GenericStats instead.
Rasmus Faber