Generics: How To Get Map Values Without Casting
I have an abstract class named BaseCode
and 2 concrete classes named Location
and Department
:
public abstract class BaseCode {
private Integer id;
...
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
...
}
public class Location extends BaseCode {
...
}
public class Department extends BaseCode {
...
}
I have an abstract class named BaseCodeCache
and 2 concrete classes named LocationCache
and DepartmentCache
. LocationCache
and DepartmentCache
will use Singleton
.
public abstract class BaseCodeCache {
...
}
public class LocationCache extends BaseCodeCache {
...
}
public class DepartmentCache extends BaseCodeCache {
...
}
- In
BaseCodeCache
, I want to havejava.util.Map
, whose value can be any type ofBaseCode
ie objectsLocation
orDepartment
objects.
QLocationCache
I want tojava.util.Map
save objectsLocation
.
QDepartmentCache
I want tojava.util.Map
save objectsDepartment
.
To accomplish this, I put this code in BaseCodeCache
:
private Map<Integer, BaseCode> idMap = new HashMap<Integer, BaseCode>();
- I want to have a way to store the value in
java.util.Map
.
To accomplish this, I put this code in BaseCodeCache
:
public void add(BaseCode baseCode) {
if (baseCode != null) {
idMap.put(baseCode.getId(), baseCode);
}
}
This is how I will use it for Location
:
Location location = new Location(); ...
LocationCache.getInstance().add(location);
This is how I will use it for Department
:
Department department = new Department(); ...
DepartmentCache.getInstance().add(department);
- I want to have a method to get all values in
java.util.Map
as ajava.util.List
.
InLocationCache
this method should returnList<Location>
.
InDepartmentCache
this method should returnList<Department>
.
This is where I am stuck. I want to create this method in BaseCodeCache
, but when this method is called through LocationCache
, it returns List<Location>
and when this same method is called through DepartmentCache
, then it returns List<Department>
. Is it possible?
I put this code in BaseCodeCache
:
public List<BaseCode> getList() {
return new ArrayList<BaseCode>(idMap.values());
}
But the above code returns List<BaseCode>
. When I call it like this:
List<Location> allLocations = LocationCache.getInstance().getList();
Then java won't let it compile and pass this error message:
Type mismatch: Cannot convert from
List<BaseCode>
toList<Location>
I can fix this by getting List<BaseCode>
and then converting it to List<Location>
by looping, but it doesn't look right.
Can this be done?
source to share
Implement using generics like this:
public abstract class BaseCodeCache<T extends BaseCode> {
private Map<Integer, T> idMap = new HashMap<>();
public void add(T baseCode) {
if (baseCode != null) {
idMap.put(baseCode.getId(), baseCode);
}
}
public List<T> getList() {
return new ArrayList<>(idMap.values());
}
}
public class LocationCache extends BaseCodeCache<Location> {}
public class DepartmentCache extends BaseCodeCache<Department> {}
This will allow you to do the following without compilation errors:
LocationCache locationCache = new LocationCache();
locationCache.add(new Location());
List<Location> locations = locationCache.getList();
Better yet, you get compilation errors if you try to add or get the wrong object type:
locationCache.add(new Department()); // won't compile
List<Department> departments = locationCache.getList(); // won't compile
source to share