Iwould like to do something like the following:
public class Test {
public static void main(String[] args) {
Map<String, Set<String>> map = new HashMap<String, Set<String>>();
map.put("key1", new HashSet<String>());
Set<String> theSet = getObjectAs(map, ***Set<String>.class***);
}
private static <T> T getObjectAs(Object object, Class<T> cls){
return cls.cast(object);
}
}
But this doesn't work, I can't get the class object out of that Set using .class (see in bold) because it is parameterized.
I want to make that method to return a Set who's type may vary (it won't always be a set of Strings) but which I know and I can give as a parameter.
Is there another way of doing something like this?
The only way you can possibly do this is to accept that you need to do unsafe casts. There is no such thing as Set<String>.class, because it would be exactly equal, in every respect, to Set.class.
The only other thing you might be able to do is use one of the "generic Class" types from a library somewhere, like Guava's TypeToken, but this wouldn't let you get around the need for unsafe casts -- it would only let you specify it with generics. Guava's TypeToInstanceMap works similarly.
Related
I have a function that uses a HashMap<Integer, ArrayList<CustomObject1>> map as a parameter. However, now functionality has been expanded and I need to call this function with a different HashMap that contains ArrayList<CustomObject2>>.
I've looked into generics and tried passing in HashMap<Integer, ArrayList<? extends CustomObjectBase>> map but I will need to know what is actually passed in so I can respond accordingly. Making another function did not work due to type erasure. I've also tried using java instanceof but that does not work either.
Any advice on how this could be solved? Thank you!
I've spent hours trying this on my own before, and it was a fruitless endeavor. It is generally preferred to instead pass the class as a parameter, like so:
public void <T> handle(HashMap<Integer, ArrayList<T>> map, Class<T> clazz)
{
// do whatever you need to, based on
}
Then, you'll call it like so:
var map1 = new HashMap<Integer, ArrayList<CustomObject1>>();
// map1 populate...
var map2 = new HashMap<Integer, ArrayList<CustomObject2>>();
// map1 populate...
var h = new Handler();
h.handle(map1, CustomObject1.class);
h.handle(map2, CustomObject2.class);
instanceof is your friend:
if (map instanceof CustomObject1) {
//...
} else {
//...
}
Also, you can use method overloading as well. You have method1 that you are talking about, yet you need to do some differentiation between the types. The custom logic that you need for can be delegated to method2 and you can implement different overloads if they are different.
Also, map.get(0).getClass().getName() can come in handy here if your array list is not empty, of course.
I need to type method signature so it accepts 2 equally typed parameters of different particular concrete subtypes.
Is it possible to code something like this with generics? How would you solve it? (The case is absolutely an example)
public <T extends List<?>> T<String> sum(T<Integer> sublistOfInts, T<Boolean> sublistOfBooleans){
/*fusion both lists*/
return sublistOfStrings;
}
EDIT: In the end, what I am looking for is a way for the compiler to pass:
ArrayList<String> myList = sum(new ArrayList<Integer>(), new ArrayList<Boolean>());
but not:
ArrayList<String> myList = sum(new ArrayList<Double>(), new ArrayList<Boolean>());
nor
ArrayList<String> myList = sum(new LinkedList<Integer>(), new ArrayList<Boolean>());
(...)
EDIT 2: I found a better example. Imagine an interface Tuple, with child classes Duple, Triple>..., it would be perfectly nice to have something like
<T extends Tuple<?>> T<String> reset( T<String> input, T<Boolean> listToNull){
T copy = input.copy();
for (int i=0; i<input.size();i++){
if (listToNull.get(i)){
copy.set(i,null);
}
}
}
What I suggest you do instead
First, get rid of the method argument generics. There's no reason to force a caller to provide ArrayList<Integer> and ArrayList<Boolean> when you want to return an ArrayList<String>. Just accept any List<Integer> and List<Boolean>, and leave it to your method to turn them into the appropriate return List.
Since you know that you want to return some sort of List of String you can write your parameter as <T extends List<String>> and your return type as simply T.
That leaves us with the hard part: getting your method to instantiate an object of unknown type. That's hard. You can't just do new T();. You need to invoke something that will produce a T on your behalf. Luckily, Java 8 provides a Functional Interface for Supplier<T>. You just need to invoke the get() method to get your ArrayList<String> or whatever else you might want. The part that's painful is that your invoker needs to provide their own Supplier. But I think that's as good as it gets in Java 8.
Here's the code:
public <T extends List<String>> T sum(
List<Integer> sublistOfInts,
List<Boolean> sublistOfBooleans,
Supplier<T> listMaker) {
T sublistOfStrings = listMaker.get();
/*fusion of both lists*/
return sublistOfStrings;
}
At least this compiles:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, LinkedListList::new);
You can even leave off the type parameter on the invocation. This compiles:
ArrayList<String> myNewList = thing.sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.sum(intList, boolList, LinkedListList::new);
Why you can't just do what you're asking
In brief, it's because type arguments can't themselves be parameterized. And that's because we don't know how many type arguments they themselves would take, nor the restrictions that might be placed on them.
Take the relatively obscure class RoleList. It extends ArrayList<Object>, so it fits List<?>. But it doesn't take a type argument at all. So if someone invoked your sum() method with RoleList, that would require in your example:
RoleList<Integer> intList = // something
RoleList<Boolean> boolList = // something
RoleList<String> myNewList = thing.sum(intList, boolList);
That clearly can't work since it requires an unparameterized type to take type arguments. And if you took off the type arguments like so:
RoleList intList = // something
RoleList boolList = // something
RoleList myNewList = thing.sum(intList, boolList);
Then your method needs to be able to accept two List<Object> arguments and return a value of List<Object>. And that violates your basic premise, that you be able to control such things.
In reality, RoleList should not be allowed here at all, because you can't ever guarantee that one instance will contain only Integers, another only Booleans, and a third only Strings. A compiler that allowed RoleList here would necessarily have weaker type checking than we have now.
So the bottom line is that you just can't do what you're asking because Java just isn't built that way.
Why that's ok
You can still get complete type safety inside your sum() method using my suggested method, above. You make sure that the incoming Lists contain only Integer or Boolean values, respectively. You make sure that the caller can rely on the return of a specific subtype of List containing only String values. All of the guarantees that make a difference are there.
There are two things that strike me about the above. How are you instantiating sublistOfStrings, and what advantages do you expect to get above using plain old inheritance?
There are a couple of ways of instantiating T<String>. You could have a factory check the class of your arguments, and instantiate it based on that. Or you could do something like:
(List<String>)sublistOfInts.getClass().newInstance()
But you can't just go new T<String>(). So you're basing the implementation of your return type off of the type of one of your arguments anyway (unless there's a way I haven't thought of).
By specifying both arguments are of type 'T' doesn't mean they're exactly of the same concrete type 'T' either. For instance
sum((int)1, (long)2L); // valid
sum((int)2, (double)2.0D); // valid ... etc
public <T extends Number> T sum(T a, T b) {
return a;
}
So you aren't enforcing that sublistOfInts and sublistOfBooleans are both of type say ArrayList, and therefore you can return an ArrayList. You still need to write code to check what type of List<?> you'll want to return based on the arguments.
I think you're better off not using generics, and using something like this:
public List<String> sum(List<Integer> sublistOfInts, List<Boolean> sublistOfBooleans) {
// Determine what subclass of list you want to instantiate based on `sublistOfInts` and `sublistOfBools`
// Call factory method or newInstance to instantiate it.
// Sum, and return.
}
You can still call it with subtypes of List<?>. I don't beleive there's any advantage you could get from generics even if Java did let you do it (which is doesn't, because it can't parameterize T like that).
I know what you have is just an example but if you only want to return a single list that contains the String value of all the contents in a group of other lists you could just specify a method that takes a varargs of unbounded lists.
public List<String> sum(List<?>... lists) {
List<String> sublistOfStrings = new ArrayList<String>();
for(List<?> list : lists) {
for(Object obj : list) {
sublistOfStrings.add(obj.toString());
}
}
return sublistOfStrings;
}
I coded the following method to convert my Arraylist into a set:
public static Set<Animal> toSet(){
Set<Animal> aniSet = new HashSet<Animal>(animals);
return aniSet;
}
I would like to do this instead :
public static Set<Animal> toSet(){
return HashSet<Animal>(animals);
}
Why do i get an error message that says it cannot find variable HashSet ? Do i need to store a variable first ?
EDIT : had to add new before my Hashset. Coding makes me feel so dumb :')
There are two problems with this code:
You forget that the animals have to come from somewhere; I don't think the first example compiles either; and
you forgot to use new when creating a new HashSet<Animal>.
This is probably the intended behavior:
public static <T> Set<T> toSet(Collection<? extends T> data){
return new HashSet<T>(data);
}
You can then call it with:
ArrayList<Animal> animals = new ArrayList<>();
//do something with the animals list
//...
Set<Animal> theSet = Foo.<Animal>toSet(animals);
by using a generic static method, you can call it with any type you like. By using Collection<? extends T> you are furthermore not limited to an ArrayList<T>, but you can use any kind of Collection (LinkedList, HashSet, TreeSet, ...). Finally the type of that collection does not even have to be animal. You could convert an ArrayList<Cat> into a HashSet<Animal>.
Note however that there is not much use in this method: calling it is not much shorter than using the constructor directly. The only real advantage I see is that you encapsulate which Set<T> you are going to use, such that if you later change your mind to TreeSet<T> all methods calling this toSet method will generate a TreeSet<T> instead of a HashSet<T>.
I have some Guava Functions like Function<String,Set<String>>. Using those with FluentIterable.transform() leads to a FluentIterable<Set<String>>, however I need a FluentIterable<String>. So my idea now would be to subclass FluentIterable<E> and add a new method transform2() which simply merges everything to one collection before returning it.
The original transform method looks like this:
public final <T> FluentIterable<T> transform(Function<? super E, T> function) {
return from(Iterables.transform(iterable, function));
}
I thought of something like this for my subclass and transform2() method:
public abstract class FluentIterable2<E> extends FluentIterable<E>
{
public final <T> FluentIterable<T> transform2(Function<? super E, Collection<T>> function) {
// (PROBLEM 1) Eclipse complains: The field FluentIterable<E>.iterable is not visible
Iterable<Collection<T>> iterables = Iterables.transform(iterable, function);
// (PROBLEM 2) Collection<T> merged = new Collection<T>(); // I need a container / collection - which one?
for(Collection<T> iterable : iterables)
{
// merged.addAll(iterable);
}
// return from(merged);
}
}
Currently I have two problems with my new subclass, marked above with PROBLEM 1 and PROBLEM 2
PROBLEM 1: The iterable field in the original FluentIterable class is private - what can I do about this? Can I create a new private field with the same name in my subclass, will this then be OK? What about methods in my subclass that call super.someMethod() which uses this field? Will they then use the field of the super class, which probably has a different value?
PROBLEM 2: I need some generic collection where I can combine the content of several collections, but collections is an interface, so I can't instantiate it. So, which class can I use there?
It would be acceptable if the solution only works with sets, though I'd prefer a solution that works with sets and lists.
Thanks for any hint on this!
Does FluentIterable.transformAndConcat(stringToSetFunction) not work for your use case?
Why subclass FluentIterable just to do this? You just need a simple loop:
Set<String> union = Sets.newHashSet();
for (Set<String> set : fluentIterableOfSets) {
union.addAll(set);
}
Use FluentIterable.transformAndConcat(f), where f is a Function mapping an element to some kind of iterable over the element type.
In your case, let's say your Function<String, Set<String>> is called TOKENIZE, and your initial Iterable<String> is called LINES.
Then to get a Set<String> holding all the distinct tokens in LINES, do this:
Iterable<String> LINES = ...;
Function<String, Set<String>> TOKENIZE = ...;
Set<String> TOKENS = FluentIterable.from(LINES)
.transformAndConcat(TOKENIZE)
.toSet();
But consider JB Nizet's answer carefully. Try it both ways and see which works better.
Let's say I want to have some kind of a cache that did something like
Map<Class<T extends SomeBaseClass>, List<T>> cache = new ...
When I try to do this is throws compile errors. I don't want to make them generics of the class I'm working with, because they don't belong there. I also will have many different kinds of T there. But I also don't want to use <? extends SomeBaseClass> because you don't have a compile time guarantee that the list going in is of the same type the class that maps to it is...
What are my options here? These terms are so overused here and on google I can't seem to search for them :-(
I also will have many different kinds of T there.
If I understand you correctly, what you want is to use generics to somehow guarantee that each entry in the map will associate a Class object with some object of the class in question.
Generics cannot do that. Let's quote your code snippet:
Map<Class<T extends SomeBaseClass>, List<T>> cache = new ...
It looks like you want each entry in the Map to associate a Class object with an instance of that class. Well, generics can't do anything to help you there, because the type parameter T in Map<Class<T>,T> will have one and only one type substituted for it when the type is specialized to a particular T. This means a specialized Map<Class<Foo>,Foo> version of the class will have a method with this signature:
public Set<Map.Entry<Class<Foo>,Foo>> entries();
And the Map.Entry<Class<Foo>,Foo>> references you get out of the Set's iterator method would have these methods:
public Class<Foo> getKey();
public Foo getValue();
In this case, well, there is only one value for Class<Foo>, which is the class object for the Foo class. But if we go with your example and use Class<T extends SomeBaseClass> doesn't really add much; getKey() will get you some Class object whose newInstance method produces something that can be cast to Foo, but there is no guarantee that getValue() will get you a value of the same runtime class, or even of a class that can be cast to the same class as the one returned by getKey().
EDIT: You probably want something like this (untested, not even tried to compile it):
public class InstanceTable {
private final Map<Class,Object> table =
new ConcurrentHashMap<Class,Object>();
public <T> void put(Class<T> klass, T value)
throws ClassCastException {
table.put(klass.cast(value));
}
public <T> T get(Class<T> klass) {
return klass.cast(table.get(klass))
}
}
This offers compile-time type-safety only in the case where you statically name the classes you're using in the source code, using the Foo.class syntax:
InstanceTable t = new InstanceTable();
t.put(String.class, "foo"); // compiles
t.put(Integer.class, "bar") // doesn't compile
String foo = t.get(String.class); // compiles
Integer one = new Integer(1);
Integer bar = t.get(one.getClass()); // doesn't compile
But understand that beyond this, it isn't using the type system to enforce the invariant you want at compilation time —it's still checking it at runtime (the calls to Class.cast()).
There are no options. This isn't avoidable, although you can wrap it up in your own API that hides the type casts...but there's no way to guarantee this sort of thing in Java at compile time.
That said, you can create an API that's typesafe for use on the outside, and suppresses warnings on the inside. (#ysdx suggested looking at Guava's ClassToInstanceMap, which is indeed a good example.) But you're not going to get something that's internally type-safe: there are going to be casts on the inside.
so you want a map of
String.class --> list of strings
Int.class --> List of Integers
Ok, no prob. But: in a particular instance of your map:
Map<K, V> x = …;
What particular, specific class is K? You want it to be a whole buch of different ones at once, which aint going to happen.
You are going to have to do some casting.
I've implemented this in TypedMap: http://blog.pdark.de/2010/05/28/type-safe-object-map/
Here is some demo code:
TypedMap map = new TypedMap();
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 ); // Look Ma, no cast!
assertEquals( expected, value );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 ); // Even with generics
assertEquals( list, valueList );
The magic is in the key:
final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );