CodyBugstein
CodyBugstein

Reputation: 23322

In Java, can I somehow move field to superclass if subclasses have different types for them?

In my Java program, I have two classes that are really similar, and it would make a lot of sense to create a superclass for them to subclass and generalize a bunch of their methods.

The only problem is that one of their key fields is implemented in one as a Queue and in one as a PriorityQueue.

Is there any way to superclass these and somehow generalize the field type and let the subclasses pick what it will perhaps on instantiation?

public Base{

}

public Sub1{
     Queue<Customer> customers;
}

public Sub2{
     PriorityQueue<Customer> customers;
}

Upvotes: 1

Views: 408

Answers (6)

Chetan Kinger
Chetan Kinger

Reputation: 15212

What you need to do is have the queue of customers as a dependency in Base and let the client code decide what queue to instantiate.

public Base{
  private Queue<Customer> customers;

  protected Base(Queue<Customer> customers) {
     this.queue = queue;
  }

}

You don't need the Sub1 and Sub2 classes since they only differ from Base in terms of the queue used. Client code can simply decide what queue to use :

Base baseWithQueue = new Base(new ArrayBlockingQueue<Customer>());
Base baseWithPriorityQueue = new Base(new PriorirityQueue<Customer>());

Upvotes: 1

ttzn
ttzn

Reputation: 2613

One solution is to give your customers attribute the general Queue type, which will give you flexibility. But if you want to enforce specific implementations in a given subclass, and carry that information in the rest of your program, you can use generic constraints :

public class Base<Q extends Queue<Customers>> {
    protected Q customers;

    public Base(Q customers) {
        this.customers = customers;
    }
}

public Sub1 extends Base<Queue<Customers>> {
   public Sub1(Queue<String> customers) {
      super(customers);
   }
}

public Sub2 extends Base<PriorityQueue<Customers>>{
   public Sub2() {
       // Sub2 only supports PriorityQueues
      super(new PriorityQueue<String>());
   }
}

This can be useful, as it allows you to call, say, customers.comparator() inside Sub2 without having to cast customers into a PriorityQueue.

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074248

Yes, since PriorityQueue implements Queue. If you want to delegate creation to subclasses, have them pass the appropriate Queue to use to the base constructor:

public class Base {
    protected final Queue<Customer> customers;

    protected Base(Queue<Customer> c) {
        this.customers = c;
    }
}

public class Sub1 extends Base {
     Sub1() {
        super(new WhateverQueue<Customer>());
     }
}

public class Sub2 extends Base {
     Sub2() {
         super(new PriorityQueue<Customer>());
     }
}

I should mention that if Sub2 needs to use methods that are specific to PriorityQueue (such as comparator) in some Sub2-specific method, it can either:

  1. Cast customers (since it knows that customers is actually a PriorityQueue<Customer>, not just a Queue<Customer>), or

  2. Declare its own instance field which will keep the typed reference:

    private PriorityQueue<Customer> priorityCustomers;
    
    Sub2() {
        super(new PriorityQueue<Customer>());
        this.priorityCustomers = (PriorityQueue<Customer>)(this.customers);
    }
    

#2 keeps the casting required to a minimum and nicely contained to just the constructor, making it easier to change PriorityQueue<Customer> to something else later.

Upvotes: 1

SamTebbs33
SamTebbs33

Reputation: 5647

Yes, if you implement the field as Queue, you can instantiate the field as a PriorityQueue in Sub2, because PriorityQueue implements Queue (source: http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html)

public Base{
     Queue<Customer> customers;
     public Base(Queue<Customers> queue){
         this.customers = queue;
     }
}

public Sub1{
    public Sub1(){
        super(new Queue<Customer>());
    }
}

public Sub2{
     public Sub2(){
        super(new PriorityQueue<Customer>());
    }
}

However, when you need to use the customers field as a PriorityQueue (rather than a Queue) inside Sub2, you will need to cast it to a PriorityQueue.

Upvotes: 1

Emile P.
Emile P.

Reputation: 3962

The PriorityQueue class implements the Queue interface, so I'd recommend making Base an abstract class and keep the customers collection (of type Queue) in there. Then instantiate customers from Sub1 or Sub2's constructor as appropriate.

Upvotes: 1

Roy Shahaf
Roy Shahaf

Reputation: 494

priorityqueue is a subclass of abstract queue which implements queue, so basically base could have a queue of customers.

Upvotes: 1

Related Questions