Given the following class:
public class TestMap<K,V> {
private HashMap<K,V> map;
public void put(K k, V v) {
map.put(k, v);
}
public V get(K k) {
return map.get(k);
}
}
and this function:
static<T extends Object> String getObj(TestMap<T, String> m, String e) {
return m.get(e);
}
Why does "get" show this error:
The method get(T) in the type MyHashMap<T,String> is not applicable for the arguments (String)
When getObj states T extends Object, and the map has been initialized with TestMap<T, String> m, why can't String be used as parameter? I cant wrap my head around why this doesnt work, as String is a subtype of Object ?
I tried extending T from a custom superclass and using a subclass instead of String.
get() requires as an argument a key, which is of type T, not String.
So either you need to change e to type T or change the type of the Map, e.g., to TestMap<String, T>.
Generics describe what an Object is, List<String> strings is a list of Strings. That gives us some convenience, we don't have to cast. String s = strings.get(0); and we get some compile-time protection. Integer i = 1; then strings.add(i); fails to compile.
The error you are getting is a compile time protection. The key you're providing is an Object, so it might be the correct type. In the case of a map, "why not check?" It is just going to hashcode and check for equality which is ok. In the case of a List though, strings.add((Object)i); needs to fail.
If the doesn't fail, then later something might call. for(String s: strings) and get a class cast exception.
To fix this you can change you V get(K k) method to a V get(Object k) the same as map uses. Your get method requires type K a map normally has a get method with a Object
As mentioned "T extends Object" is the same as just "T". You use the extends when you want to put a lower bound on something. public <T extends Number> int sum(List<T> numbers). In this case "T" can be any class that extends numbers. We can do Number n = numbers.get(0); because we know it is at least a number. But we could not do numbers.add(n); because while T is at least a number, it could be something else like an Integer, Double, Number, BigDecimal etc.
Related
I am using java 8 and I would like to detect subtle class differences at compile time modifying withProperty() header. This code is working but I would like to force a compilation error in main() function because this::getInteger returns an Integer and the second argument is a String.
import java.util.function.Function;
public class MatcherProperty<T, K> {
public static <T, K> MatcherProperty<T, K> withProperty(
Function<T, K> mapper,
K expected
){
return new MatcherProperty<>();
}
private Integer getInteger(Object object) {
return 1;
}
public void main() {
withProperty(this::getInteger, "Random string");
}
}
I would like to avoid (if possible) a third argument in withProperty() function specifying the class type or something like this. Maybe K is translated to Object, the superclass of Integer an String. What is actually happening under the hoods? Is it possible to force a compilation error in this case?
Thanks in advance.
There is no compile error in your current code because the result of the call to withProperty is ignored.
If you would try to assign the result like this:
MatcherProperty<Object, Integer> mp = withProperty(this::getInteger, "Random string");
then you'd get a compilation error because the String argument doesn't match type K which is Integer in the result.
If you would try to assign the result like this:
MatcherProperty<Object, String> mp = withProperty(this::getInteger, "Random string");
then you'd get a compilation error because the Integer result of the function given as first argument doesn't match type K which is String in the result.
You can only make the assignment compile by using a common super type such as Object or Serializable:
MatcherProperty<Object, Serializable> mp = withProperty(this::getInteger, "Random string");
You can't force people to assign the result of course. You can add a Class<K> parameter to make them choose a class (such as Integer.class or String.class) but even then, they can just pass Serializable.class or Object.class instead:
public static <T, K> MatcherProperty<T, K> withProperty(
Class<K> clazz,
Function<T, K> mapper,
K expected
)
withProperty(String.class, this::getInteger, "Random string"); // doesn't compile
withProperty(Integer.class, this::getInteger, "Random string"); // doesn't compile
withProperty(Serializable.class, this::getInteger, "Random string"); // compiles
If you don't tell the compiler somehow what type K is (using class argument or assignment of the return value which is of type K) then it will infer the common type, Serializable in this case.
I'm little confused about how the generics works? I'm learning about function API in java and there I just test Function interface and got confused about compose method that how the generics is working in compose method.
Reading the generics on the java official tutorial website I realize that if we have any generic type in the method return or parameters we have to declare that type in the signature of method as explained below.
Here is the method I read in official docs tutorial.
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
Above method have two types, K, V which are declared in the signature after the static keyword as but when I read java Function API there is one method called compose and the signature of the compose is as
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
1) The first question where is the T & R declared? which are being used in the return type and in the parameter. Or my understanding is wrong?
Then I read more in generics tutorials and then I try to understand the concept of super and extends in generics and read here then I test compose method more and then confused again about how the super and extends works in the compose method?
public static void main(String... args){
Function<Integer, String> one = (i) -> i.toString();
Function<String, Integer> two = (i) -> Integer.parseInt(i);
one.compose(two);
}
As above I have declared two Function with lamdas. One is having Integer input and String output the other one is reversed from it.
2) The second question is that how Integer and String are related to extends and super? There is no relation between String and Integer class no one is extending each other then how it is working?
I tried my best to explain my question/problem. Let me know what you didn't understand I will try again.
Where are T and R defined?
Remember, compose is declared in the Function interface. It can not only use generic parameters of its own, but also the type's generic parameters. R and T are declared in the interface declaration:
interface Function<T, R> {
...
}
What are ? extends and ? super?
? is wildcard. It means that the generic parameter can be anything. extends and super give constraints to the wildcard. ? super V means that whatever ? is, it must be a superclass of V or V itself. ? extends T means that whatever ? is, it must be a subclass of T or T itself.
Now let's look at this:
Function<Integer, String> one = (i) -> i.toString();
Function<String, Integer> two = (i) -> Integer.parseInt(i);
one.compose(two);
From this, we can deduce that T is Integer and R is String. What is V? V must be some type such that the constraints Function<? super V, ? extends T> is satisfied.
We can do this by substituting the argument we passed in - Function<String, Integer> - to get String super V and Integer extends Integer.
The second constraint is satisfied already while the first constraint now says that String must be a super class of V or String itself. String cannot have subclasses so V must be String.
Hence, you can write something like:
Function<String, String> f = one.compose(two);
but not
Function<Integer, String> f = one.compose(two);
When you compose a Function<Integer, String> and a Function<String, Integer> you cannot possibly get a Function<Integer, String>. If you try to do this, V is automatically inferred to be Integer. But String super Integer is not satisfied, so the compilation fails. See the use of the constraints now? It is to avoid programmers writing things that don't make sense. Another use of the constraints is to allow you to do something like this:
Function<A, B> one = ...
Function<C, SubclassOfB> two = ...
Function<SubclassOfC, B> f = one.compose(two);
There is no relationship between Integer and String in this case, it's all about V.
1) The compose function is part of Interface Function<T,R>. As you can see in documentation for this interface:
Type Parameters:
T - the type of the input to the function
R - the type of the result of the function
2) The super and extends constraints in questions aren't applied to T & R, they're applied to the generic type parameters of a function that you pass in as an argument to the compose function.
Basically this means that if you have:
Function<ClassA, ClassB> one;
Function<SomeSuperClassOfC, SomeSubclassOfA> two;
then it's valid to call
Function<ClassC, ClassB> three = one.compose(two)
I will try to explain from zero;
interface Function<T, R> - this is interface with one method, which must be implemented R apply (T);
in Java prior to 8 we must write:
Function<Integer, String> one = new Function<Integer, String>() {
#Override
public String apply(Integer i) {
return i.toString();
}
};
now you can use it:
String resultApply = one.apply(5);
now, I think, you get the idea.
I want to compare two arguments in less method which are of type Comparable. But if I use comapreTo it is giving me error in type casting:
private boolean less(Comparable<T> u, Comparable<T> v) {
return u.compareTo(v) < 0;
}
I fixed the issue by type casting the argument passed into compareTo. But still it is giving me warning (Type safety: Unchecked cast from Comparable to T). Why is it so. Am I doing something wrong. What is the best way to achieve this.
private boolean less(Comparable<T> u, Comparable<T> v) {
return u.compareTo((T) v) < 0;
}
Since u has type Comparable<T>, u.compareTo() takes a T. You're passing it v, a Comparable<T>, which is not compatible.
You might be assuming in your mind that a type that implements Comparable is comparable to itself. But there is no such requirement on a technical level. It's perfectly legal for me to make a type Foo, that is Comparable<Bar>, i.e. it compares to Bar, where Foo and Bar are completely unrelated. Trying to pass a Foo into a Foo object's compareTo will crash.
If you want to make a constraint that the two arguments are of the same type which is comparable to itself, you would do something like
private <T extends Comparable<? super T>> boolean less(T u, T v) {
return u.compareTo(v) < 0;
}
However, for the purposes of this function, the constraint that the two arguments are of the same type is not technically necessary (although it should be true in all normal use cases). And in generics we always want to use the least restrictive bound possible, which is, as other answers have mentioned,
private <T> boolean less(Comparable<T> u, T v) {
return u.compareTo(v) < 0;
}
In your comments, you mentioned you are comparing elements from a Comparable<T>[]. That is not a type of array that allows you to compare elements from. You just know that it's an array of elements that are comparable to a certain T, but not comparable to themselves. Much more useful would be to have a T[], where T has a bound that it is comparable to itself:
class Whatever<T extends Comparable<? super T>> {
//...
T[] yourArray;
}
The initial error is correct, and the warning is correct to: your trick is very unsafe: you can't cast v to T.
Because your method takestwo parameters u and v, each one is an instance of a class, which implements compareTo ( some instance of T).
precisely: u can compare to T, v can compare to T.
But, inside, you want to compare u and v. There is absolutely no garantee you can do it.
This, for example, is correct:
private static <X> boolean less(Comparable<X> u, X v)
{
return u.compareTo(v) < 0;
}
I'm implementing a weighted probability algorithm, so I created a generic Pair class. Since the probability is calculated using numbers, the value of Pair would always be an Integer, but I wanted it to work the way where the key could be any Object. This is what I got:
class Pair<K, Integer> {
public K k;
public java.lang.Integer v;
public Pair(K k, java.lang.Integer v) {
this.k = k;
this.v = v;
}
// getters and other stuff
}
It works fine, but I find it weird that no matter what I type instead of the Integer part in the first line, it works the same. Am I missing something? Is there a better way to do that?
class Pair<K, Integer>
is equivalent to
class Pair<K, V>
where the name of the second generic parameter would happen to be Integer instead of V (and thus hiding the type java.lang.Integer, which forces you to use java.lang.Integer instead of just Integer in the code, to avoid a conflict).
Your class should only have one generic parameter:
class Pair<K>
You use generics when you may accept any type.
But since you know that type to be Integer, you do not need to make it generic.
The new version with one generic type argument will look like this:
public class Pair<T> {
public T t;
public int v;
public Pair(T t, int v) {
this.t = t;
this.v = v;
}
// ...
}
It is good practice, when you have just one generic type argument, to name it with the "T" letter.
Also, you can now use int instead of Integer.
The way you use it, Integer is just a type variable, same as K.
If you don't need the type of the second value of the pair to be a parameter, then don't declare it as type parameter, but just use Integer in the code:
class IntPair<K> {
private K first;
private Integer second;
public Integer someIntegerSpecificFunction() {
// do stuff to internalPair.second
}
K getFirst() {
return first;
}
Integer getSecond() {
return second;
}
}
The following gives me an error message:
public static List<Comparable<?>> merge(Set<List<Comparable<?>>> lists) {
List<Comparable<?>> result = new LinkedList<Comparable<?>>();
HashBiMap<List<Comparable<?>>, Integer> location = HashBiMap.create();
int totalSize;
for (List<Comparable<?>> l : lists) {
location.put(l, 0);
totalSize += l.size();
}
boolean first;
List<Comparable<?>> lowest; //the list with the lowest item to add
int index;
while (result.size() < totalSize) {
first = true;
for (List<Comparable<?>> l : lists) {
if (! l.isEmpty()) {
if (first) {
lowest = l;
}
else if (l.get(location.get(l)).compareTo(lowest.get(location.get(lowest))) <= 0) { //error here
lowest = l;
}
}
}
index = location.get(lowest);
result.add(lowest.get(index));
lowest.remove(index);
}
return result;
}
The error is:
The method compareTo(capture#1-of ?) in the type Comparable<capture#1-of ?> is not applicable for the arguments (Comparable<capture#2-of ?>)
What's going on here? I made the type of everything Comparable so I could call .compareTo and sort this list. Am I using generics incorrectly?
List<?> means "List of anything", so two objects with this type are not the same: One could be a list of String, the other a list of BigDecimal. Obviously, those are not the same.
List<T> means "List of anything but when you see T again, it's the same T".
You must tell the compiler when you mean the same type in different places. Try:
public static <T extends Comparable<? super T>> List<T> merge(Set<List<T>> lists) {
List<T> result = new LinkedList<T>();
HashBiMap<List<T>, Integer> location = HashBiMap.create();
[EDIT] So what does <T extends Comparable<? super T>> List<T> mean? The first part defines a type T with the following properties: It must implement the interface Comparable<? super T> (or Comparable<X> where X is also defined in terms of T).
? super T means that the type which the Comparable supports must T or one of its super types.
Imagine for a moment this inheritance: Double extends Integer extends Number. This is not correct in Java but imagine that Double is just an Integer plus a fraction part. In this scenario, a Comparable which works for Number also works for Integer and Double since both derive from Number. So Comparable<Number> would satisfy the super part for T being Number, Integer or Double.
As long as each of these types support the Comparable interface, they also satisfy the first part of the declaration. This means, you can pass in Number for T and the resulting code will also work when there are Integer and Double instances in the lists. If you Integer for T, you can still use Double but Number is not possible because it doesn't satisfy T extends Comparable anymore (the super part would still work, though).
The next step is to understand that the expression between static and List just declares the properties of the type T which is used later in the code. This way, you don't have to repeat this long declaration over and over again. It's part of the behavior of the method (like public) and not part of the actual code.