Restrict a generic Class parameter to classes that implement Map
I'm trying to write a Map
builder. One of the constructors will allow the client to specify the type of Map
they wish to build
public class MapBuilder<K, V> {
private Map<K, V> map;
/**
* Create a Map builder
* @param mapType the type of Map to build. This type must support a default constructor
* @throws Exception
*/
public MapBuilder(Class<? extends Map<K, V>> mapType) throws Exception {
map = mapType.newInstance();
}
// remaining implementation omitted
}
The intent is that it should be possible to construct instances of the builder with:
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(LinkedHashMap.class);
or
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(HashMap.class);
It seems that the type signature of the constructor argument doesn't currently support this, because the line above causes a "Cannot resolve constructor" compilation error.
How can I change my constructor so that it accepts classes that implement Map
only?
java generics
add a comment |
I'm trying to write a Map
builder. One of the constructors will allow the client to specify the type of Map
they wish to build
public class MapBuilder<K, V> {
private Map<K, V> map;
/**
* Create a Map builder
* @param mapType the type of Map to build. This type must support a default constructor
* @throws Exception
*/
public MapBuilder(Class<? extends Map<K, V>> mapType) throws Exception {
map = mapType.newInstance();
}
// remaining implementation omitted
}
The intent is that it should be possible to construct instances of the builder with:
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(LinkedHashMap.class);
or
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(HashMap.class);
It seems that the type signature of the constructor argument doesn't currently support this, because the line above causes a "Cannot resolve constructor" compilation error.
How can I change my constructor so that it accepts classes that implement Map
only?
java generics
4
Why not use aSupplier
instead? That way, the users can passLinkedHashMap::new
and not be bothered with reflection.
– RealSkeptic
18 hours ago
add a comment |
I'm trying to write a Map
builder. One of the constructors will allow the client to specify the type of Map
they wish to build
public class MapBuilder<K, V> {
private Map<K, V> map;
/**
* Create a Map builder
* @param mapType the type of Map to build. This type must support a default constructor
* @throws Exception
*/
public MapBuilder(Class<? extends Map<K, V>> mapType) throws Exception {
map = mapType.newInstance();
}
// remaining implementation omitted
}
The intent is that it should be possible to construct instances of the builder with:
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(LinkedHashMap.class);
or
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(HashMap.class);
It seems that the type signature of the constructor argument doesn't currently support this, because the line above causes a "Cannot resolve constructor" compilation error.
How can I change my constructor so that it accepts classes that implement Map
only?
java generics
I'm trying to write a Map
builder. One of the constructors will allow the client to specify the type of Map
they wish to build
public class MapBuilder<K, V> {
private Map<K, V> map;
/**
* Create a Map builder
* @param mapType the type of Map to build. This type must support a default constructor
* @throws Exception
*/
public MapBuilder(Class<? extends Map<K, V>> mapType) throws Exception {
map = mapType.newInstance();
}
// remaining implementation omitted
}
The intent is that it should be possible to construct instances of the builder with:
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(LinkedHashMap.class);
or
MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(HashMap.class);
It seems that the type signature of the constructor argument doesn't currently support this, because the line above causes a "Cannot resolve constructor" compilation error.
How can I change my constructor so that it accepts classes that implement Map
only?
java generics
java generics
asked 18 hours ago
DónalDónal
122k156485750
122k156485750
4
Why not use aSupplier
instead? That way, the users can passLinkedHashMap::new
and not be bothered with reflection.
– RealSkeptic
18 hours ago
add a comment |
4
Why not use aSupplier
instead? That way, the users can passLinkedHashMap::new
and not be bothered with reflection.
– RealSkeptic
18 hours ago
4
4
Why not use a
Supplier
instead? That way, the users can pass LinkedHashMap::new
and not be bothered with reflection.– RealSkeptic
18 hours ago
Why not use a
Supplier
instead? That way, the users can pass LinkedHashMap::new
and not be bothered with reflection.– RealSkeptic
18 hours ago
add a comment |
3 Answers
3
active
oldest
votes
Use a Supplier
instead of a Class
:
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
map = supplier.get();
}
Which then can be called like this:
MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);
This is also safer, because a Class<Map>
could have no default constructor, which would throw an error (which is not very responsive code)
1
You can get rid of the wildcard altogether:public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for exampleclass Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen
– Lino
18 hours ago
1
Granted. It would still fail even if you use a lambda (Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.
– ernest_k
18 hours ago
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
add a comment |
The following will work:
public MapBuilder(Class<? extends Map> mapType) throws Exception {
map = mapType.newInstance();
}
1
Will produce an unchecked warning though
– Lino
18 hours ago
add a comment |
The problem is that LinkedHashMap.class
is
Class<LinkedHashMap>
and not something like
Class<LinkedHashMap<Integer, String>>
These are also inconvertible types (so you can't cast it) and there's no way to get an instance of the latter.
What you can do is change the constructor to
public MapBuilder(Class<? extends Map> mapType) throws Exception
Generics are erased at run-time so at run-time all Map
s will behave just like Map<Object, Object>
anyway. Therefore it doesn't matter that the class you're constructing from is using the raw type.
By the way, Class::newInstance
is deprecated. Use
mapType.getConstructor().newInstance()
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54745717%2frestrict-a-generic-class-parameter-to-classes-that-implement-map%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Use a Supplier
instead of a Class
:
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
map = supplier.get();
}
Which then can be called like this:
MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);
This is also safer, because a Class<Map>
could have no default constructor, which would throw an error (which is not very responsive code)
1
You can get rid of the wildcard altogether:public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for exampleclass Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen
– Lino
18 hours ago
1
Granted. It would still fail even if you use a lambda (Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.
– ernest_k
18 hours ago
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
add a comment |
Use a Supplier
instead of a Class
:
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
map = supplier.get();
}
Which then can be called like this:
MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);
This is also safer, because a Class<Map>
could have no default constructor, which would throw an error (which is not very responsive code)
1
You can get rid of the wildcard altogether:public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for exampleclass Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen
– Lino
18 hours ago
1
Granted. It would still fail even if you use a lambda (Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.
– ernest_k
18 hours ago
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
add a comment |
Use a Supplier
instead of a Class
:
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
map = supplier.get();
}
Which then can be called like this:
MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);
This is also safer, because a Class<Map>
could have no default constructor, which would throw an error (which is not very responsive code)
Use a Supplier
instead of a Class
:
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
map = supplier.get();
}
Which then can be called like this:
MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);
This is also safer, because a Class<Map>
could have no default constructor, which would throw an error (which is not very responsive code)
answered 18 hours ago
LinoLino
9,10422039
9,10422039
1
You can get rid of the wildcard altogether:public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for exampleclass Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen
– Lino
18 hours ago
1
Granted. It would still fail even if you use a lambda (Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.
– ernest_k
18 hours ago
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
add a comment |
1
You can get rid of the wildcard altogether:public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for exampleclass Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen
– Lino
18 hours ago
1
Granted. It would still fail even if you use a lambda (Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.
– ernest_k
18 hours ago
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
1
1
You can get rid of the wildcard altogether:
public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
You can get rid of the wildcard altogether:
public MapBuilder(Supplier<Map<K, V>> supplier)
– ernest_k
18 hours ago
1
1
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for example
class Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen– Lino
18 hours ago
@ernest_k you can't. Without the wildcard it would fail if you provide an instance of for example
class Sup implements Supplier<HashMap<Integer, Integer>>
. It's a very rare case, but it can happen– Lino
18 hours ago
1
1
Granted. It would still fail even if you use a lambda (
Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.– ernest_k
18 hours ago
Granted. It would still fail even if you use a lambda (
Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);
- but this is something I'd personally be happy to have no support for (that's an opinion). This is a good answer, btw.– ernest_k
18 hours ago
2
2
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
@ernest_k Thank you :), I agree though that in a small project it would not make a difference (using the wildcard or not) but when writing an API which is used across different projects it is the way to go and makes the lifes of people using your API a lot easier
– Lino
17 hours ago
add a comment |
The following will work:
public MapBuilder(Class<? extends Map> mapType) throws Exception {
map = mapType.newInstance();
}
1
Will produce an unchecked warning though
– Lino
18 hours ago
add a comment |
The following will work:
public MapBuilder(Class<? extends Map> mapType) throws Exception {
map = mapType.newInstance();
}
1
Will produce an unchecked warning though
– Lino
18 hours ago
add a comment |
The following will work:
public MapBuilder(Class<? extends Map> mapType) throws Exception {
map = mapType.newInstance();
}
The following will work:
public MapBuilder(Class<? extends Map> mapType) throws Exception {
map = mapType.newInstance();
}
answered 18 hours ago
EranEran
285k37463552
285k37463552
1
Will produce an unchecked warning though
– Lino
18 hours ago
add a comment |
1
Will produce an unchecked warning though
– Lino
18 hours ago
1
1
Will produce an unchecked warning though
– Lino
18 hours ago
Will produce an unchecked warning though
– Lino
18 hours ago
add a comment |
The problem is that LinkedHashMap.class
is
Class<LinkedHashMap>
and not something like
Class<LinkedHashMap<Integer, String>>
These are also inconvertible types (so you can't cast it) and there's no way to get an instance of the latter.
What you can do is change the constructor to
public MapBuilder(Class<? extends Map> mapType) throws Exception
Generics are erased at run-time so at run-time all Map
s will behave just like Map<Object, Object>
anyway. Therefore it doesn't matter that the class you're constructing from is using the raw type.
By the way, Class::newInstance
is deprecated. Use
mapType.getConstructor().newInstance()
add a comment |
The problem is that LinkedHashMap.class
is
Class<LinkedHashMap>
and not something like
Class<LinkedHashMap<Integer, String>>
These are also inconvertible types (so you can't cast it) and there's no way to get an instance of the latter.
What you can do is change the constructor to
public MapBuilder(Class<? extends Map> mapType) throws Exception
Generics are erased at run-time so at run-time all Map
s will behave just like Map<Object, Object>
anyway. Therefore it doesn't matter that the class you're constructing from is using the raw type.
By the way, Class::newInstance
is deprecated. Use
mapType.getConstructor().newInstance()
add a comment |
The problem is that LinkedHashMap.class
is
Class<LinkedHashMap>
and not something like
Class<LinkedHashMap<Integer, String>>
These are also inconvertible types (so you can't cast it) and there's no way to get an instance of the latter.
What you can do is change the constructor to
public MapBuilder(Class<? extends Map> mapType) throws Exception
Generics are erased at run-time so at run-time all Map
s will behave just like Map<Object, Object>
anyway. Therefore it doesn't matter that the class you're constructing from is using the raw type.
By the way, Class::newInstance
is deprecated. Use
mapType.getConstructor().newInstance()
The problem is that LinkedHashMap.class
is
Class<LinkedHashMap>
and not something like
Class<LinkedHashMap<Integer, String>>
These are also inconvertible types (so you can't cast it) and there's no way to get an instance of the latter.
What you can do is change the constructor to
public MapBuilder(Class<? extends Map> mapType) throws Exception
Generics are erased at run-time so at run-time all Map
s will behave just like Map<Object, Object>
anyway. Therefore it doesn't matter that the class you're constructing from is using the raw type.
By the way, Class::newInstance
is deprecated. Use
mapType.getConstructor().newInstance()
edited 18 hours ago
answered 18 hours ago
MichaelMichael
20.4k83471
20.4k83471
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54745717%2frestrict-a-generic-class-parameter-to-classes-that-implement-map%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
4
Why not use a
Supplier
instead? That way, the users can passLinkedHashMap::new
and not be bothered with reflection.– RealSkeptic
18 hours ago