Here is the snippet (from Java Generics and Collections) and below is question:
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder()
{
return new Comparator<T>() {
public int compare(T o1, T o2) { return o2.compareTo(o1); }
};
}
As I follow you have a method using a wildcard that extends comparable where it compares types T and "above." It then returns a new Comparator which apparrently has a method inside it that returns value of a compareTo using two objects of type T. So questions:
Is this defining a method inside a method? I thought you couldn't do
that in Java.
Is it returning a method? Something I also thought you couldn't do
in Java.
How do I use this? The book uses examples where other, similar,
methods have parameters.
This has no parameters, so where do o1 and o2 come from?
It is an anonymous class:
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return new Comparator<T>() {
public int compare(T o1, T o2) {
return o2.compareTo(o1);
}
};
}
You are:
Declaring a method compare in the anonymous class, which is in the method reverseOrder.
Returning an instance of the anonymous class.
Declaring o1 and o2 as the parameters of compare.
Questions / Attempted Answers:
As I follow you have a method using a wildcard that extends comparable where it compares types T and "above."
Methods can extend nothing, so no, this is not correct. The method is generic and can be used with concrete class that extends Comparable.
It then returns a new Comparator which apparrently has a method inside it that returns value of a compareTo using two objects of type T. So questions:
OK, kind of correct.
1) Is this defining a method inside a method? I thought you couldn't do that in Java.
It is not a method in a method. It's a method that returns an object of the type Comparator that has its own compare method.
2) Is it returning a method? Something I also thought you couldn't do in Java.
No, it returns an object of an anonymous inner class.
3) How do I use this? The book uses examples where other, similar, methods have parameters. This has no parameters, so where do o1 and o2 come from?
The o1 and o2 are the declared parameters of the Comparator object's compare method.
Following tutorials about Comparators and Anonymous classes might help:
http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/
http://www.dummies.com/how-to/content/how-to-use-an-anonymous-class-in-java.html
Related
How can I bind a Java Supplier to an existing instance of an Object? For example, if I want to write my own compareTo() method with this header:
public static int myCompareTo(Object o1, Object o2, Supplier<Comparable> supplier) {...}
I want be able to call it like:
myCompareTo("Hello", "Hello2", String::length);
where String (with the capital letter) is a class and no object. So how can I bind the instance o1 to the supplier?
Here's what you were searching for (I believe):
public static <T, U extends Comparable<U>> int compare(T o1, T o2, Function<T, U> mapper) {
return mapper.apply(o1).compareTo(mapper.apply(o2));
}
You can call that like so:
compare("str1", "str2", String::length); // 0
Thanks for your answers. Actually I figured it out now. I wanted to have the supplied object instances (o1 and o2) to execute the given method. I found out that Supplier was the wrong interface instead I had to use Function. Here you can see my working simplified example:
public static <T> int myCompareTo(T o1, T o2, Function<T, Comparable> getter) {
return getter.apply(o1).compareTo(getter.apply(o2));
}
The reason, the interface has to be Function and not Supplier is, that only Function is equivalent to a lambda expression taking an object and calls the referenced method on the object.
For example, if you define the method reference as:
Function<TypeOfInstance, ReturnTypeOfReferencedMethod> methodReference = TypeOfInstance::referencedMethod();
then the equivalent lambda expression being executed is:
(instance) -> instance.referencedMethod()
Additional Information:
Edit: I know I could have done the same by using Comparator, but this example is very simplified. In my application a Function of this kind is neccessary. I had to create a compareTo function that sorts an ArrayList by more than one attribute because the main sorting attribute may not be unique in the list. I want to share my code with you, because I think it can be a interesting insight for you.
public static <T> int ultimateCompare(T o1, T o2, Function<T, Comparable>... getters) {
for (Function<T, Comparable> getter : getters) {
int result = getter.apply(o1).compareTo(getter.apply(o2));
if (result != 0) return result;
}
return 0;
}
With this for example, you can sort a list of persons by last name and if two of them are identical, you can use the first name to sort. With this solution you can change sorting at runtime.
Actually a more correct way to define your method would be:
private static <T, U extends Comparable<? super U>> int myCompareTo(T left, T right, Function<T, U> fu) {
return Comparator.comparing(fu).compare(left, right);
}
You can use
Comparator.comparing(String::length);
to obtain a comparator instance which you can pass to the method.
I am currently learning lambda expressions on JDK 1.8. I have come across some code I have found that I do not understand.
Here is the code:
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.lang.Comparable;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws Exception
{
List<String> list = Arrays.asList("a", "b", "c");
sort(list, Comparable::<String>compareTo);
}
interface MyComparable {
public <T extends Comparable<T>> int compare(T obj1, T obj2 );
}
public static <T extends Comparable<T>> void sort(List<T> list, MyComparable comp) {
int n = comp.compare("5","2");
System.out.println(n);
}
}
comp.compare("5", "3") eventually executes "5".compareTo("2").
My understanding was the compiler needs to find a static method with same signature as
public <T extends Comparable<T>> int compare(T obj1, T obj2 );
I have created such a method and it works. I do not understand why the java compiler calls "5".compareTo("2"). Their method signitures are not the same.
Any information as to why the compiler generates this kind of code?
If you are trying to learn method references, you should resort to some sort of learning material, e.g. Oracle’s Java tutorial. There you find:
Kinds of method references
Kind
Example
Reference to a static method
ContainingClass::staticMethodName
Reference to an instance method of a particular object
containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type
ContainingType::methodName
Reference to a constructor
ClassName::new
So you see, method references are not restricted to static methods.
Your method reference Comparable::<String>compareTo matches the kind “Reference to an instance method of an arbitrary object of a particular type”.
At this point it’s worth noting that you are actually referencing the method Comparable.compareTo as if you had written Comparable::compareTo. Since the referenced method has no type parameters on its own, your type argument has no effect. E.g. you could write Comparable::<Button>compareTo with the same result.
The referenced method has the functional signature (Comparable,Comparable) → int as it consumes two Comparables when invoking Comparable.compareTo on one Comparable, passing the second Comparable as argument (and it will return an int). This matches the functional signature of your interface
interface MyComparable {
public <T extends Comparable<T>> int compare(T obj1, T obj2 );
}
so the method reference can be used in this context.
I have simplified the functional signatures; actually they are (T,T)→int using <T extends Comparable<T>>, therefore you can only compare two instances of the same concrete Comparable implementation using this function.
So you're wondering how a method reference with different signature than expected, can be sent as lambda expression. But they don't need to be fully the same. Basically, only list of parameters and return type matters:
A lambda expression can be assigned to a target type T if all of the following conditions hold:
T is a functional interface type
The lambda expression has the same number of parameters as T's method, and those parameters' types are the same
Each expression returned by the lambda body is compatible with T's method's return type
Each exception thrown by the lambda body is allowed by T's method's throws clause
from here (section 4)
List of parameters of your compare() (String, String) can be matched to list of parameters of String#compareTo() (this, String) and their return types are also the same (int), so one can be used as lambda when another is expected.
I thought it would be nice to use generics to write a quick sort. I'm just learning generics, and it seems like you can only test whether one generic value is equal to another. Is there a way to see if one is greater then another? I wrote a simple isBig method as follows
public Boolean isBigger( T b)
{
if (b>x) // error message here
return true;
return false;
}
Actually, generics are wrong for this purpose. Instead you should have a look at the interfaces Comparable and Comparator - Using these is the way to implement a generic sorting algorithm (as java.util.Collections does)
You can't use a greater than operator on a generic type parameter, since that operator is only defined for primitives.
You can use methods such as compare or compareTo if your generic type parameter is bound by an interface that contains that method (such as Comparable).
You need to compare by an object's field that you consider relevant to the comparison. For sorting through a list of objects by one field you can use Comparator.
How to use Comparator in Java to sort
public static <T extends Comparable<? super T>>
boolean isBigger(T x, T y) {
return x != null && (y == null || x.compareTo(y) > 0);
}
public static <T>
boolean isBigger(T x, T y, Comparator<? super T> cmp) {
return cmp.compare(x, y) > 0;
}
How can I have a method with two parameters, with both parameters having the same concrete type?
For example,
boolean equals(Object a, Object b)
allows for a of any type and b of any type.
I want to force such that a and b have the same concrete type. I tried
<T> boolean equals(T a, T b)
and inputting a Date and a String to that method, expecting a compile-time error, but I get no errors, since T will resolve to ? extends Serializable & Comparable, since both Date and String implements Serializable and Comparable.
You can't, basically. There's no way of doing that. Even if you could do it for a simple call to prohibit arguments of different types, it could always be bypassed using a cast:
equals((Object) date, (Object) string)
If you're interested in the execution-time types of the arguments, you can only test that at execution time. There's no way of the compiler knowing whether an argument of type Date has a value which is a reference to precisely a java.util.Date or some subclass.
The thing is your method's signature becomes
<? extends Object> boolean equals(? extends Object a, ? extends Object b)
which doesn't give you any options. Even if you call
equals(new Date(), "hello world");
the compiler doesn't even need to break a sweat and determine the lowest common ancestor for your parameter types.
Edit
Interesting fact. I knew what I wrote above was true, but it still looked a bit weird. So I tested
<T> boolean equals(T a, T b) {
return true;
}
<T,E> boolean equals(T a, E b) {
return true;
}
Which the compiler yelled at. The reason is that the compiler indeed doesn't make a difference and just rewrites both methods as
boolean equals(? extends Object a, ? extends Object b)
which after type erasure becomes
boolean equals(Object a, Object b)
which is the exact same signature. Indeed, if I keep your method equals(T,T) and I add another method with signature equals(Object, Object) the compiler continues to say that I have the same method declared elsewhere.
Long story short, your method equals(T,T) is the same as equals(Object, Object) due to type erasure and thus you can't force the same parameter type, at least at compile time, unless you specifically implement equals methods for each.
As other users have mentioned, there is no way to force this for a particular method. But you can, if it is possible for you, move this method to a separate parameterised class and then use the parameter in the method signature.
For example: This is the method where I wanted to enforce same argument types. (I was trying to get diff between o1 and o2)
public List<Change> getChanges(Object o1, Object o2);
I encapsulated this method inside a parameterised class.
public class DiffGenerator<T> {
public List<Change> getChanges(T o1, T o2) {
//code
}
}
This can be used as:
List<Change> changes = new DiffGenerator<MyClass>().getChanges(myClassOld, myClassNew);
I have a method called getMember which may return any Comparable object, String, Integer etc.
I can't figure out what to use as type parameter so compareTo will work. Code below is not working
Comparable<? extends Comparable<?>> m1 = column.getMember(o1);
Comparable<? extends Comparable<?>> m2 = column.getMember(o2);
int compareTo = m1.compareTo(m2);
In case I wasn't clear, m1 and m2 will always be same type. Comparing without type parameters works fine, I just wanted to know what to put in <>
I think what you need here is a generic method. Whatever method this section of code is in must be generified. Here's my best shot, in absence of your source for the getMember method.
public <T extends Comparable<? super T>> void doSomethingTheOPWants() {
...
T m1 = column.getMember(o1);
T m2 = column.getMember(o2);
int compareTo = m1.compareTo(m2);
}
With my current knowledge of the code, I don't think I can create a relevant/worthwhile test, so I'll leave that up to OP.