Use of anonymous class in sort method - java

why if I put an anonymous class with Comparator in the sort method of List the compiler show me an error?
My code:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
l.sort(Comparator<String> c= new Comparator<>() { //compiler error
public int compare(String a, String b) {
return b.compareTo(a);
}
});
}
The sort method accepts a Comparator. If I write this code, it compiles:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
l.sort(new Comparator<String>() { //it's ok
public int compare(String a, String b) {
return b.compareTo(a);
}
});
}
Or this code:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
Comparator <String> c = new Comparator<String>() {
public int compare(String a, String b) {
return b.compareTo(a);
}
};
l.sort(c); //it's ok
}
Why does it happen?
Thanks a lot!

The first one fails as it is an assignment. The sort method expects an object of the Comparator class. So when you say sort(new Comparator), you are creating a new Comparator and immediately passing it to the sort method. When you have Comparator c = new Comparator () and then you have sort(c), you create an new Comparator, store it in variable c, and pass it to the sort method.
The first segment of code tries to both assign a new Comparator to variable c and pass it to method sort, the syntax of java does not allow this. It is analagous to having a method that takes one integer as argument and writing foo(int bar = 7). It doesnt quite make sense.

In the second example you are not giving the comparator to the method, you are assigning it to the 'c' reference. In the third example you are assigning it to c reference but then giving it to the sort method.
Hope this helps :)

The problem here is not that you are passing an anonymous class.
l.sort(Comparator<String> c= new Comparator<>() { ... });
This is attempting to declare a variable, c. Variable declarations are statements, and so they cannot be used inside expressions. (You also can't use the diamond operator when declaring an anonymous class).
If you want to declare a variable to hold that comparator, and assign it inside the sort invocation, you can, like so:
Comparator<String> c;
l.sort(c = new Comparator<String>() { ... });
but I suspect that isn't what you intended.

Related

Java Can I set variable in class depending on the input

I would like following effect -> I have object of class FluidArray which will be an array, but depending on the input it will be either int array or String array:
FluidArray XXX = new FluidArray;
XXX.YYY[] might be either String or int
In this case variable YYY of class XXX might be int array or String
Can I somehow declare variable type depending on some choice?
public class FluidArray
{
VarType YYY;
public static void FluidArray(int a)
{
double[] YYY = new double[15];
}
public static void FluidArray(String a)
{
String[] YYY = new String[15];
}
}
Let's say I want to make a sort method.
I input there unsorted array.
I take out sorted array.
The catch is I might want to sort String, double or int array and I don't want to write 3 sorting methods - I thought that my sorting method might work on some defined object and this object will be either String, double int depending on my choice.
I am trying to use Generic type, I got so far sth. like this:
public class test
{
public static void main(String[] args)
{
FluidArray<Integer> arrTest = new FluidArray<>();
arrTest.arr[1]=2;
arrTest.arr[2]=3;
arrTest.arr[3]=4;
}
public static class FluidArray<arrType>
{
public arrType[] arr = (arrType[])new Object[15];
}
}
I don't understand, why I can't get access to the array, compiler ends when inserting first value.
Read up on Generics. Thats what they are supposed to do

Why does Java allow a compare(String, String) Comparator method to take arguments of Object type?

So here is my code:
public class Demo {
public static final Comparator<String> SORT_BY_LENGTH = new SortByLength();
private static class SortByLength implements Comparator<String>{
public int compare(String w, String v) {
return w.length()-v.length();
}
}
public static void main(String[] args) {
Object o1 = "abc", o2 = "bc";
Comparator c = SORT_BY_LENGTH;
System.out.println(c.compare(o1, o2));//OK, output 1
}
}
So what confuses me is that the signature of the compare() method takes 2 String variables as argument. However, even when I input 2 arguments of Object type, it still works. Why is that?
PS: if I define some ordinary method as follows, then the compiler will complain there is an error, because Object cannot be converted to String.
public static int foo(String w, String v) {
return w.length()-v.length();
}
public static void main(String[] args) {
Object o1 = "abc", o2 = "bc";
System.out.println(foo(o1, o2));// compiling error!
}
You can use Object as arguments here because you used the raw type of Comparator, which is something that you generally should not do.
Here is where you did it:
Comparator c = SORT_BY_LENGTH;
See how there are not generic arguments to Comparator? That is a sign of using raw types. You can assign any instance of a parameterised type, like SORT_BY_LENGTH to a raw type. However, since the raw type Comparator has no generic parameters, its compare method takes two Objects instead of Strings. So you can theoretically pass anything into compare.
However, if the wrong type of object goes into the method, an exception will be thrown. Try putting two new Object() in there!
This is why you should not assign parameterised types to raw types. They are not type safe. You should do something like this instead:
Comparator<String> c = SORT_BY_LENGTH;
or use SORT_BY_LENGTH directly.
If you made the above change, the call to compare will not compile anymore, because it is expecting Strings. You should change the type of o1 and o2 to String.

Provide arguments with a method

Is there a way in Java to create a method that would return the list of parameters of another method such that I am able to call
anotherMethod(method())
where anotherMethod has arbitrary arguments like
public void anotherMethod(int a, int b, String c)
And what is if the types stay the same, like with
public int add(int a, int b, int c)
If there is no such way, how could I model the list of parameters such that it would work? Is it a List or an array or something else?
If the number of parameters is fixed at the call site, you could use varargs
int add(int... numbers)
otherwise you'd use an array or collection
int add(int[] numbers)
You can then of course have another method provide the value of these parameters:
add(someOtherMethod())
Varargs
Java has a built-in feature to denote a variable length of arguments. It is called varargs (documentation) (variable arguments) and it only works if the type stays the same. The syntax for a method is like this:
public int add(int... values)
Note the int... values which denotes varargs. A caller can now call the method like
add(null) // Passing null
add(values) // Passing an int[]
add() // No arguments
add(a) // One int
add(a, b) // Two ints
add(a, b, c) // Three ints
add(a, b, c, d) // Four ints
...
Note the three special cases null, int[] and empty.
What Java does is it will convert the arguments into an array. So inside the method values will be a regular int[]. You could thus implement the method like
public int add(int... values) {
int sum = 0;
for (int value : values) {
sum += value;
}
return sum;
}
If you, as a caller, want to pass the return value of a function you just need to make sure that it returns an array like int[]. So the following would work:
public int[] valueProvider() {
int[] values = ...
return values;
}
and then call it like
int sum = add(valueProvider());
Collection, Iterable and Stream
Besides that, if you don't want to use varargs or arrays, you can use Collections (documentation). A collection may be a List or a Set and so on. For example you could declare
public int add(Collection<Integer> values)
and feed it like
Collection<Integer> values = new ArrayList<>();
values.add(1);
values.add(2);
int sum = add(values);
An Iterable<Integer>, in contrast to Collection<Integer> would even be more flexible.
Using a Stream (documentation) would also work like a charm and is probably one of the most flexible variants since the source of a stream could be anything and nearly anything of the standard library supports a stream representation.
Changing type
Now note that what you searched for in the beginning, a method that is able to feed arbitrary arguments, is not possible in Java.
The main problem is that the types may change, so you may have a method like
public void doSomething(int first, String second, File third)
and you won't be able to feed the method with varargs, Collections or any of the presented methods.
In that case you will need a wrapper class like
public class DoSomethingArguments {
private int mFirst;
private String mSecond;
private File mThird;
public DoSomethingArguments(int first; String second, File third) {
this.mFirst = first;
this.mSecond = second;
this.mThird = third;
}
// Some getters
}
(or a generic tuple class, a triple in this case)
But then you would need to change the method to
public void doSomething(DoSomethingArguments arguments)
what is probably not what you wanted since you probably intended to not change the signature of doSomething.
But unfortunately there is no way to feed a method like this in such a way.
There is nothing that works the way you wish for at compile time. As the other answers are pointing out, there are varargs. But that is just syntactical sugar. That is just the compiler implicitly creating an array of a certain type for you.
But beyond that, there is reflection. Reflection allows you to dynamically inspect classes and methods at *runtime.
In other words: you can do something like
Object whatever = ...
Class<?> someClass = whatever.getClass();
And now you can ask someClass about the methods it has. And which parameters they need.
But as said: all of that is runtime only. And it the reflection APIs are very easy to get wrong. And you only find out at runtime, when some exception is thrown.
There is not direct way to pass multiple values in the way you want. But you can use a indirect way to pass a group of values of different type. I can think of two ways but their can be more.
Firs - Use a map, just insert the values you want to pass in the collection and pass the collection to the second method.
Second - Create a bean (Java POJO) to pass as parameter to the consuming method.
A small sample code.
class Sample{
private int a;
private String b;
private int c;
Sample(int a,String b,int c){
this.a = a;
this.b = b;
this.c = c;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
}
public class PassingExample {
public void consumerofInputs (Map<Integer, Object> input)/*(int a, String b, int c)*/{
System.out.println("I use three different inputs : int, string and int");
for (Map.Entry<Integer, Object> entry : input.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue());
}
}
public Map producingInput() {
Map<Integer, Object> input = new HashMap<Integer, Object>();
input.put(1, 10);
input.put(2, "input");
input.put(3, 89);
return input;
}
public Sample createClassAsInput(){
Sample input = new Sample(10,"class-input",30);
return input;
}
public void useSampleAsInput(Sample input){
System.out.println("\nUsing Class as input \nInt::"+input.getA()+"\nString::"+input.getB()+"\nInt::"+input.getC());
}
public static void main(String[] args) {
PassingExample example = new PassingExample();
example.consumerofInputs(example.producingInput());
example.useSampleAsInput(example.createClassAsInput());
}
}

how to order set of objects based on array of integers

I have a class called Variable
Class Variable{ private String name; private int[] domain; //...etc}
which represents variable in specific structure (constraint satisfaction problem).
I have instantiated set of variables in ArrayList< Variable > and filled up an array of integers.
ArrayList<Variable> vars=new ArrayList<Variable>();
Variable a=new Variable("A",new int[]{1,2});
vars.add(a);
// Define all variables;
int[] cons=new int[vars.size()];
for(int i=0;i<cons.length;i++)
cons[i]=number_of_constraints(vars.get(i));
// cons contains number of involved constraints for each variable
Now I need to sort them descending based on the number of constraints.
In other words: Given list of Objects [(A,{1,2}) , (B,{3,4}) , (C,{5,6}) ] and an array of integers cons={1,2,0} how to sort the list of objects descending based on the array of integers?
Use a sorted collection like a TreeSet
class Variable {
private String name;
private int[] domain;
};
final Set<Variable> variables = new TreeSet<Variable>( new Comparator<Variable>() {
public int compare(Variable o1, Variable o2) {
//Do comparison here
//return -1 if o1 is less than o2
//1 if o1 is greater than o2
//0 if they are the same
}
});
Now you have a sorted Set of your Variables. This is guaranteed to always be sorted.
If you would like to keep Class Variable intact, the following code will sort the given vars outside:
Collections.sort(vars, new Comparator<Variable>() {
public int compare(Variable var1, Variable var2) {
return var2.number_of_constraints() - var1.number_of_constraints();
}});
If you can change Class Variable, let it implement interface Comparable:
class Variable implements Comparable<Variable> {
//...
public int compareTo(Variable other) {
return this.number_of_constraints() -
other.number_of_constraints();
}
}
Then you can sort vars by:
Collections.sort(vars);
As far as a Variable contains numOfConstraints, according to your code, you can make your Variable class implement Comparable interface, like
public class Variuable implements Comparable<Variable> {
private int numOfConstraints;
public int compareTo(Variable other){
if(this == other) { return 0; }
return (numOfConstraints == other.numOfConstraint) ? 0 : ((numOfConstraints > other.numOfConstraint) ? 1 : -1);
}
}
And then use the utility method java.util.Collections.sort(vars);, that's it.
Your Variable class should implement the Comparable interface,
When it does you should implement the compareTo method.
After that you can sort it by calling the Collection.sort method.
If you want to sort by a permutation if your indexes that's just a matter of creating a new ArrayList and mapping each index to the new index (using a for loop)
Here is such a (generic) method
public static <T> ArrayList<T> permutate(ArrayList<T> origin,int[] permutation){
ArrayList<T> result = new ArrayList<T>(permutation.length);
for(int j=0;j<permutation.length;j++){
result.add(null);
}
for(int i=0;i<permutation.length;i++){
result.set(i, origin.get(permutation[i]));
}
return result;
}
You can do myArrayList= permutate(myArrayList, new int{1,2,3});
Here is example usage in a more basic use case (integers):
public static void main(String... args){
ArrayList<Integer> origin = new ArrayList<>(4);
origin.add(1);
origin.add(2);
origin.add(3);
origin.add(4);
int[] per = new int[]{2,1,3,0};
origin = permutate(origin,per);
System.out.println(Arrays.toString(origin.toArray())); //prints [3,2,4,1], your permutation
}

store comparators objects in array

I have defined 4 comparators for my object like this:
public static Comparator<mObject> comp0 = new Comparator<mObject>() {
#Override
public int compare(mObject l, mObject r) {
...compare
}
};
public static Comparator<mObject> comp1 = new Comparator<mObject>() {
#Override
public int compare(mObject l, mObject r) {
...compare
}
};
public static Comparator<mObject> comp2 = new Comparator<mObject>() {
#Override
public int compare(mObject l, mObject r) {
...compare
}
};
public static Comparator<mObject> comp4 = new Comparator<mObject>() {
#Override
public int compare(mObject l, mObject r) {
...compare
}
};
Now i want to create an array with the 4 comparators ,like this :
public final static Comparator<mObject>[] Object_comparators = { comp0,
comp1,
comp2,
comp3};
but Eclipse underlines everything between {..} like it is an error .Why is it ,and how could i fix it ?
You cannot create an array of classes with a parameterized generic type.
If you don't mind losing type safety, you can do this:
Comparator[] list = new Comparator[4];
But my preferred strategy would be to use a List:
List<Comparator<mObject>> list = Arrays.asList(comp0, comp1, comp2, comp3);
As Mark Elliot said you can't use generics type in arrays.
This is because of type erasure, while arrays have their type known at runtime.
Because of type erasure, the generic type is never known at runtime.
This means the generics are just useful at compilation time, and that by some tricks, you can insert Animal objects into a List<Comparator>
Btw you can't declare the creation of an anonymous array without specifying the type of the array, since there is no type erasure for the array type.
Thus it is not allowed to create an array using generic types and could lead to some unexpected behaviors.
Think about it: if you create an array of car (Car[]) then you can cast it to Object[]. That does not mean you can put objects in that array, since the type of the array is still known at runtime. You will get an ArrayStoreException if you try to put a dog on that array.
Now what if we created an array Car<Ferrari>[]? You can still cast it to Object[].
It works like the Car[] array except in this case, when you have casted it into an Object[], you WILL be able to put objects of type Car<Renault> at runtime, since the generic type is not known!
I think Java prevents us from such behaviors.
But you can still use a array of generics reference!!!
public static void main(String[] args) {
Comparator<String> NORMAL = new Comparator<String>() {
#Override
public int compare(String s, String s1) {
return s.compareTo(s1);
}
};
Comparator<String> REVERSE = new Comparator<String>() {
#Override
public int compare(String s, String s1) {
return - s.compareTo(s1);
}
};
Comparator<String>[] comparators = new Comparator[] {NORMAL,REVERSE};
List<String> strings = Arrays.asList("f","d","c","a","e","b");
TreeSet<String> normalSet = new TreeSet<String>(comparators[0]);
normalSet.addAll(strings);
for ( String s : normalSet ) {
System.out.println("normal : " + s);
}
TreeSet<String> reverseSet = new TreeSet<String>(comparators[1]);
reverseSet.addAll(strings);
for ( String s : reverseSet ) {
System.out.println("reverse : " + s);
}
}
Produces the output:
normal : a
normal : b
normal : c
normal : d
normal : e
normal : f
reverse : f
reverse : e
reverse : d
reverse : c
reverse : b
reverse : a
So you CAN use arrays with generics, but Java seems to prevent you from doing such a thing.
I guess it's because initially the array contract may be something like this:
If you create an array of type X, you will NEVER EVER be able to put
anything in it that IS-NOT-AN X. If you try, you'll get an ArrayStoreException
Thus allowing arrays with generics creation would lead to a different rule like:
If you create an array of type X<Y>, you will NEVER EVER be able to
put anything that IS-NOT-AN X. If you try, you'll get an
ArrayStoreException. But you CAN add both X<Y> and X<Z> objects!
As my previous code shows it, we CAN'T create arrays with generics, but we can create arrays without generics, and use it as an array with generics.
Comparator<String>[] comparators = new Comparator[] {NORMAL,REVERSE};
I guess it's somehow a way for Java to tell us "if you know what you'r doing, then ok you can do it"

Categories