views:

182

answers:

5

I have 2 classes

public class Customer{
  ...
  public String getCustomerNumber();
  ...
}

public class Applicant{
   ....
   private Customer c;
   public Customer getCustomer(){ return c; }
   ...
}

When presented with a list of customers or applicants I want a function which iterates the list and does something with the CustomerNumber.

I've tried overloading the function

public void processCustomerNumbers(List<Customer> custList)
...

public void processCustomerNumbers(List<Applicant> appList)
...

but these are seen as duplicate methods... is there a nice way of doing this rather than just having 2 differently named functions?

+5  A: 

The thing about generics in Java is that generic types are erased at runtime, so both of these methods compile to the same signature. You will need to have separate method names, or check the type of the list elements at runtime.

danben
Java did not always have generics; when they were added, the resulting bytecode was not changed.
Dolph
Yes, that's the reason for using type erasure - to avoid breaking backward compatibility with pre-generics code.
danben
The type are erased at runtime. Method overload selection is done at compile time. Therefore, there is enough information for the compiler to decide, even with erasure. However, there are binary representation and compatibility issues if this language change was to happen.
Tom Hawtin - tackline
+4  A: 

Generics have what is known as type erasure - List<Customer> and List<Applicant> are the same type, the compiler just places compile-time restrictions on what you can do with them.

You could check the type of the first object in the list and call a (differently-named) internal method based on that.

Anon.
Yeah, that was something I started looking at, checking the class and altering behaviour. Came to SO hoping there was an more elegant solution, if nothing comes up I'll continue down this route.
MadMurf
As other question, override selection is done at compile time so erasure does not rule it out.
Tom Hawtin - tackline
+5  A: 

If you make both classes implement a common interface,

interface CustomerNumber {
    String getCustomerNumber();
}

public class Customer implements CustomerNumber {
  ...
  public String getCustomerNumber();
  ...
}

public class Applicant implements CustomerNumber {
   ....
   private Customer c;
   public Customer getCustomer() { return c; }
   public String getCustomerNumber() { return getCustomer().getCustomerNumber(); }
   ...
}

then you might be able to do what you want with just a single method:

public void processCustomerNumbers(List<? extends CustomerNumber> appList) {
    for (Customer c: appList) {
        processCustomerNumber(c.getCustomerNumber());
    }
}
finnw
Good suggestion.
Software Monkey
A: 

Before coming into the method names , the class hierarchy is little bit confusing...

public class Customer{          
  ...          
  public String getCustomerNumber();          
  ...          
}          

public class Applicant{          
   ....          
   private Customer c;          
   public Customer getCustomer(){ return c; }          
   ...          
}    

Why should applicant and Customer be different objects ? Can you tell the relation between these objects ?

Sreejesh
+2  A: 

One way to workaround this issue would be to define custom list types like this:

class CustomerList extends ArrayList<Customer> {
    ...
}

class ApplicantList extends ArrayList<Applicant> {
    ...
}

Then the following overloading would be legal:

public void processCustomerNumbers(CustomerList custList)

public void processCustomerNumbers(ApplicantList appList)

However, I don't think that this would be a good idea. For a start, it hardwires particular implementation classes into your application's APIs.

A better approach is to define a common interface for Customer and Applicant that allows you to process them with one processCustomerNumbers method. (As described at length in other answers.)

Stephen C