1

I am trying to generically get vehicles by tags. How come I can't use the generic Collection type to get vehicles? The CarTypeTags and TruckTypeTags are hashmaps of CarType class and TruckType class which extend from VehicleType indexed by string (tag). Is there a way I can get the cars or trucks assigned to the types collection and return it?

Bonus: Does clazz.getClass() return the class of the caller to this method?

public <T> Collection<T> getVehicleByTag(String tag) {
        T clazz;
        Collection<T> types;
        if (clazz.getClass() == Car.class) {
            types = (CarTypeTags.get(tag)); // error here: type mismatch
        } else if (clazz.getClass() == Truck.class) {
            types.addAll(TruckTypeTags.get(tag)); // error here too
        } 
        if (types != null) {
            return types;
    }
    return new ArrayList<T>();
}
15
  • 3
    Java generics can't do what you want here. Type erasure means that types is a Collection<Object>. There is nothing available at compile time to help it figure out the correct types. Incidentally, this type of design with if on a class type usually has a better solution with inheritance. It's a code smell. Commented Jun 17, 2013 at 19:48
  • 3
    I don't see how clazz.getClass() would do anything other than throw a NPE... Does any of this actually compile? I think you need to spend some time learning how generics are supposed to work. Commented Jun 17, 2013 at 19:51
  • 1
    Generally a generic method has an argument that allows the compiler to infer the return type... Commented Jun 17, 2013 at 19:58
  • 1
    Instead of using a String, why not use a type-safe token of some sort? I'm not sure how the rest of your code is structured, but this is definitely a design smell. If you want to retrieve objects from a collection by their class, you should identify them by their class. Commented Jun 17, 2013 at 20:35
  • 1
    Also also: please consider editing the additional information in your comments explaining what you're trying to accomplish into your question if you can - it's easier than piecingit together from the discussion. Commented Jun 17, 2013 at 20:38

2 Answers 2

2

Taking a wild guess at what you're roughly going for, you want to use some sort of type token somewhere:

class Vehicle {
    String tag;
}

class Car extends Vehicle { ... }

class Truck extends Vehicle { ... }

class Tag<T> {
    Class<T> tagType;
    String tagName;
}


List<Vehicle> vehicles = new ArrayList<Vehicle>();

vehicles.add(new Car("tag1"));
vehicles.add(new Truck("tag1"));
vehicles.add(new Car("tag2"));
vehicles.add(new Truck("tag2"));

public <T> List<T> getVehiclesByTag(Tag<T> tag) {
    List<T> result = new ArrayList<>();
    for (Vehicle vehicle : vehicles) {
        if (tag.tagType.isInstance(vehicle) && tag.tagName.equals(vehicle.tag))) {
            result.add(tag.tagType.cast(vehicle));
        }
    }
    return result;
}

(Constructors, properties, etc omitted for brevity.)

The above will filter both on the tag name and the expected return type. If a single tag is only supposed to map to one vehicle type, you can leave out the isInstance() check.

Obviously the code is more of a sample of what functions you'll need to use - since I have no idea what CarTypeTags.get() etc. are supposed to do I can't reproduce your use case exactly.

As far as your bonus question goes: no. You can get the class of the caller by either poking at the stack trace (slow and ugly and fragile), or by passing it in explicitly in the token.

Sign up to request clarification or add additional context in comments.

Comments

1

It's hard to know what's going on without seeing more code...

But it looks like your method should do something like this:

public Collection<Vehicle> getVehicleByTag(String tag) {
    if (carTypeTags.containsKey(tag)) {
        return carTypeTags.get(tag);
    }
    else if (truckTypeTags.containsKey(tag)) {
        return truckTypeTags.get(tag);
    }
    else {
        return new ArrayList<Vehicle>();
    }
}

In the above code, Vehicle should be one of two things:

  1. a class that both Cars and Trucks extend
  2. an interface implemented by both Cars and Trucks

As I wrote this example, it occurred to me that the type mismatch error you're seeing probably has to do with the contents of your CarTypeTags array. It looks like your TruckTag map contains collections of Trucks, whereas you CarTag map contains only single Cars. If types is a Collection and CarTypeTags contains Cars (not collections of Cars) then that line will not work.

Either way, it's clear from your example code that you don't quite understand how generics are meant to be used in Java. You should never be comparing classes or using the instanceof method in a generic method (with very very very rare exceptions).

Also...

You should begin the names of all objects with lowercase letters in Java!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.