1

I know the title is weird, I had no idea what else to write. I'm relatively new to java so I apologize if this is something very basic. I've tried my best to explain the point through code. The question here is -

This compiles,

// WAY 1
Map<MyType, MyType> myMap = (Map) new MyMap();

This does not,

// WAY 2
Map<MyType, MyType> myMap2 = (Map<MyType, MyType>) new MyMap();

Firstly, why such behavior. And secondly, way 2 allows me to write my wanted code in one line as written in method WhatIWant, while way 1 does not, again as written in method WhatIWant. Can I write that code in one line? If yes, how. If not, why not.

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

// A class not created by me but in a library so i cannot use generic types as i cannot change stuff here
class MyMap extends LinkedHashMap<Object, Object> {
}

// has methods which i need for processing on myMap
class MyType {
    int myMethod() {
        return -1;
    }
}

class Scratch {
    public static void main(final String[] args) throws Exception {
        // WAY 1: 
        // compiles 
        // WARNING : Unchecked assignment: 'java.util.Map' to 'java.util.Map<MyType, MyType>'
        Map<MyType, MyType> myMap = (Map) new MyMap();

        // WAY 2:
        // does not compile 
        // ERROR: Inconvertible types; cannot case 'MyMap' to 'java.util.Map<MyType, MyType>'  
        Map<MyType, MyType> myMap2 = (Map<MyType, MyType>) new MyMap();
    }

    public static void WhatIWant() {
        // to be able to write code below in one line
        Map<MyType, MyType> myMap = (Map) new MyMap();
        myMap.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> entry.getKey().toString(),
                        entry -> entry.getValue().myMethod()
                ));

        // I thought it would work if i used WAY 2 like
        ((Map<MyType, MyType>) new MyMap()).entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> entry.getKey().toString(),
                        entry -> entry.getValue().myMethod()
                ));
        // but as you saw above in WAY 2, it does not compile, how can i do this in one line
    }
}
4
  • 1
    just a question.... why do you want to iterate over an empty map? if you have just created the new myMap object it will not have any element Commented Dec 30, 2019 at 12:05
  • Why do you need the MyMap class. Why no use LinkedHashMap<MyType,MyType> directly? Commented Dec 30, 2019 at 12:06
  • @TheOni It's just an example. These are not the actual classes. There's a library whose classes are of similar types. MyMap and MyType are library classes. Commented Dec 30, 2019 at 12:23
  • @Eran There's a library which has it's own type similar to MyMap. It has a method which i use which returns MyMap. Hence i can only decide how to use it, not make changes in existing library. Commented Dec 30, 2019 at 12:24

2 Answers 2

2

If you want it in "one line" (aka 1 statement, 5 lines), double-cast it:

((Map<MyType, MyType>) (Map) new MyMap()).entrySet().stream()
        .collect(Collectors.toMap(
                entry -> entry.getKey().toString(),
                entry -> entry.getValue().myMethod()
        ));

The first cast uses a raw Map, so the <Object, Object> generic type parameters are discarded. This is valid for backwards compatibility, but does generate a compiler warning saying you shouldn't do that:

Map is a raw type. References to generic type Map<K,V> should be parameterized

The second cast then applies a new set of generic type parameters. This is valid for backwards compatibility, but does generate a compiler warning saying that you're on your own, since the generic parameter types are not enforced:

Type safety: Unchecked cast from Map to Map<MyType,MyType>

The reason you can't cast directly is because Map<MyType,MyType> is not a subtype of Map<Object,Object>, i.e. it is an impossible cast in the Java type system. See Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?

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

Comments

0

you need a few changes in your class to workaround

// instead Object use your own type
    class MyMap extends LinkedHashMap<MyType, MyType> {
    }

now you can directly access/perform way 2 without casting :

Map<MyType, MyType> myMap2 = new MyMap();

and here as well:

new MyMap().entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> entry.getKey().toString(),
                        entry -> entry.getValue().myMethod()
                ));

1 Comment

As i've written in my code. MyMap and MyType are library classes and i cannot touch them. All i can do is decide how to use them. I cannot make any changes to them.

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.