I'm trying to write a generic class in Java. A few methods in that class require that T extends Comparable<T>. How can I make it such that T is required to be comparable only if one of those methods is used? Or maybe there's some other way I should organize my class?
Here's the class I'm trying to implement. Its and array that I plan to use on both comparable and non-comparable types.
// I know Java has its own containers, but this
// is homework and I'm not allowed to use them
class Array<T>
{
// Some methods that pose no
// special restrictions on T
// These require that T be comparable
public Array<T> union(...) {...}
public Array<T> intersect(...) {...}
}
You can hide type T for method. T of Test is not the same as T of CompareMethodhere.
public static class Test<T> {
<T extends Comparable<T>> void compareMethod(T t, Class<T> classt) {
}
void normalMethod(T t) {
}
}
Now example
Test<String> test = new Test<String>();//Comparable class
test.compareMethod("",String.class);//works fine
Test<Random> tes1t = new Test<Random>();//Non Comparable class
tes1t.compareMethod(new Random(),Random.class);//Compilation error here
tes1t.normalMethod(new Random());//Works fine
new Test<Random>().compareMethod("",String.class);// Not a good but can be valid
new Test<String>().compareMethod(new Random(),Random.class);//Compilation error here
Update:
After being cursed about this solution I did some search in java API and this practice gets followed for toArray() method
ArrayList<String> string = new ArrayList<String>();
string.toArray(new Integer[5]);<--Illegal however <T> is hide by toArray method
Edit: It looks like this is possible after all (see AmitD's post). But anyway, other possible solutions are
Refactor the methods requiring comparable into a subclass
Just use casts in the relevant methods, meaning that that part will only be checked at runtime.
It wont be possible through normal method such as using comparable.
It would be better if you share what is the exact requirement.
If Sorting in ArrayList/Arrays are your goal, then Comparing Non Comparable classes is useless. Sorting can only be done in objects of the same or sub types.
But if you are going to use compare for checking if the objects are equal or not then I'll suggest that you override equals(Object O) method.
Related
It seems like you usually implemented the java.lang.Comparable interface without specifying the type parameter.
public abstract class Area implements Comparable {
#Override
public int compareTo(Object other) {
if (other instanceof Area)
return new Double(getArea()).compareTo(other.getArea());
return -1; // or something else
}
abstract public double getArea();
}
Since I only want to compare apples with apples, I think it would make sense to specify the type.
public abstract class Area implements Comparable<Area> {
#Override
public int compareTo(Area other) {
// ...
If I want to introduce another class to compare Area with, I thought I could do the following:
public abstract class Area implements Comparable<Area>, Comparable<Volume> {
#Override
public int compareTo(Area other) {
// ...
}
#Override
public int compareTo(Volume other) {
// ...
}
}
But the java compiler tells me:
Area.java:2: error: repeated interface
public abstract class Area implements Comparable<Area>, Comparable<Volume> {
^
Area.java:2: error: Comparable cannot be inherited with different arguments: <Area> and <Volume>
Are there any drawbacks specifying the type argument for the generic interface?
Why won't Java allow me this multiple implementation?
Note: I'm using Java version 1.7.0_45
No, it's not a drawback of specifying the generic - it's actually a feature. Also, I don't recall any drawback for using generics in interfaces, other than the well-known fact you can't instantiate a generic type nor create a generic array (but that's more a problem of implementation, not the interface itself).
It's due to type erasure. Comparable<Area> and Comparable<Volume> is essentially the same class for the VM, and, shortly after checking validity, also for compiler.
If you want to have two distinct comparable interfaces implemented, just use Comparators for them - it's generally easier to maintain composition than inheritance in classes.
For some applications (distinguishing generics at run-time) you may also try subclassing them, e.g. ComparableArea extends Comparable<Area> & ComparableVolume extends Comparable<Volume>, but that would, in this particular case, cause more trouble than it would solve IMO, since you'd still get Comparable cannot be inherited with different arguments error - but at least you could differentiate those interfaces by e.g. instanceof.
I think this way java is saying that related classes can be Comparable, but using artificial Comparator we can do more comparisons among unrelated classes.
So we should implement generic interface of related classes (classes within the same inheritance hierarchy). In case we want to add an artificial implementation, add an interface which can be passed through (so have pair of family of interfaces like Comparable and Comparator).
If you want to use sort() method from Arrays class you MUST implement Comparable interface. This is a really good idea - you can't sort objects if they're not compatible. So you can't sort if you can't compare it's references. In this case interface is used like compatibility checker.
The question is - how can I make:
a class with a method (may do something with 2 objects)
interface that checks if this 2 objects are comatible (have compatible references)
add to my class "must use this interface" rule, so you can't use this class method without implementing specific interface, just like Comparable class does ?
Example:
public class Employee implements Comparable<Employee> {
//fields
//setters, getters
// this method must be implemented to use Arrays.sort()
public int compareTo(Employee other) {
return Double.compare(salary, other.salary);
}
}
An interface can't check anything.
You can write this:
interface One { }
interface Two { }
SomeType someMethod(One one, Two two) { ... }
The compiler will not allow anybody's code to call someMethod(a, b) unless it can prove that a is an instance of some class that implements One and b is an instance of some class that implements Two.
Is that what you're asking?
Added Info:
Arrays.sort(Object[] a) is different: The compiler does not know whether the elements in the array implement the Comparable interface or not. That information is not available until run-time.
I don't know how java.util.Arrays.sort() does it, but you you want to do the same thing in your own code, you can write this:
interface One { }
interface Two { }
SomeType someMethod(Object oneAsObject, Object twoAsObject) {
One one = One.class.cast(oneAsObject);
Two two = Two.class.cast(twoAsObject);
...
}
This is different from my first example because the compiler will let you pass in any type of object, but the function will throw a ClassCastException at run time if the wrong type of object is passed.
But why would you want to wait until run time to find an error that you could have found at compile time? (e.g., why wait until the lander is descending toward the Martian surface to find a fatal flaw that you could have found before it was launched?)
Responding to your comment:
Yes, what I mean is: if you wan't to use sort() you must implement comparable. So the question is - can I determine, that you must use some X interface if you want to use my X method.
You could do this with an abstract class.
public interface MyInterface {
public void methodA();
}
public abstract class MyAbstractClass implements MyInterface {
public void methodB(
System.out.println("Pop");
)
}
Anything that wants to use methodB must extend MyAbstractClass, and therefore must implement MyInterface.
In Java, is it possible to specify a generic method that matches lists containing any objects except enums?
public class Foo {
public enum MyEnum {
ENUM_VALUE
}
public static <T ...generics magic...> void bar(List<T> argument) {
// .. code
}
public static void test() {
Foo.bar(Arrays.asList(new Object())); // OK
Foo.bar(Arrays.asList(new Exception())); // Still OK
Foo.bar(Arrays.asList(MyEnum.ENUM_VALUE)); // Compiler error, please?
}
}
I'm under the impression that Java generics does not support <T does not extend Something>-style syntax, but I'd like to be know for sure - and preferably be proven wrong :).
If I can't implement this limitation with generics, what is the best possible workaround? My current plan is below; is a better implementation possible?
public static <T> void bar(List<T> argument, Class<T> argumentType) {
if (Enum.class.isAssignableFrom(argumentType)) {
throw new IllegalArgumentException("Enums are disallowed.");
}
}
Edit: I'm interested in this because I have an API that allows the consumer to:
Pass in a List of T and specify whether to return references to the T's in the list or clones of the T's:
public <T> void setList(List<T> contents, boolean returnClones)
Retrieve the T's from the list:
public <T> T get(int index)
Of course, if T is an Enum, then specifying returnClones with true doesn't make sense - we can't create a clone of the Enum. There are also other cases where cloning doesn't work (e.g. a no-args constructor is not available). I'm trying to make the API as robust as possible.
Edit #2: I know Enums are immutable; I'm looking for the best way of preventing the caller from accidentally specifying invalid arguments to the API. The caller should never
Pass in a List<SomeEnumType> and specifying that clones should be returned. This doesn't make sense.
Pass in a List<SomeObjectWithoutNoArgsConstructor> and specifying that clones should be returned. This makes sense but is something we cannot support (it might be possible to clone the objects, but without a no-args constructor we don't know how).
Cloneable is a good suggestion, but the purpose of the interface is different: "A class implements the Cloneable interface to indicate to the Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class." (Cloneable JavaDoc). Since we're using Dozer for our cloning instead of calling clone() on the source object, I think Cloneable would unfortunately end up hurting more than helping in this case.
I did once a refactoring, where a long parameter changed to an Object. The solution was to do something like:
/**
* Not for enums.
* #param x use not an enum.
*/
#Deprecated
public <E extends Enum<E>> void f(E x) {
throw new IllegalArgumentException("...");
// Or better throw new UnsupportedOperationException("...");
}
public <T> void f(T x) {
...
}
With IDE support this will do nicely.
Best to mention in javadoc and exception message what to use instead, the other f or so.
Using Java 6 SE, I have a class that contains a Vector. There is a class method that simply calls the contained Vector's addAll() method with the same parameters for each. The Vectors can be of either type "Superclass" or type "Subclass" and I want the addAll() methods to support both. This whole setup might look like this:
public class VectorAContainer(){
this.vectorA = new Vector<Superclass>();
}
public void addAll(Vector<Superclass> vector){
this.vectorA.addAll(vector);
}
Then I want to call a line of code like this:
VectorAContainer container = new VectorAContainer();
container.addAll(vectorB); //where vectorB is a Vector<Subclass>
The problem is, Eclipse gives the following error:
"The method addAll(Vector<Superclass>) in the type
VectorAContainer is not applicable for the arguments
(Vector<Subclass>)."
Strangely, I have found that addAll() works if it's NOT inside of a class:
//No container with an addAll(), calling directly on vectorA
this.vectorA.addAll(vectorB); //No errors!
I don't see a difference between the two above cases. I would like to get the first way to work because I have a much more elaborate class that contains a Vector like the one above. I would greatly appreciate any help you all could provide!
You need to accept a covariant generic parameter:
public void addAll(Collection<? extends Superclass> c)
You can see this in the declaration of addAll:
boolean addAll(Collection<? extends E> c)
It's because Superclass is a lower bound annotation meaning classes that are Super to Vector. If vectorB is a subclass to Vector then it won't work. AbstractList and AbstractCollection are Super to Vector. I believe you want to use.
public void addAll(Vector<? extends Vector> vector){
vectorA.addAll(vector);
}
P.S. Vector is an obsolete class. You should use ArrayList if you don't need a thread safe implementation.
*P.P.S. You don't need to use "this" unless you have two variables with the same name (e.g.
private Variable variableA;
public void example(Variable variableA){
this.variableA = variableA;
}
We have a discussion in office and cannot understand which approach is better
I have a class (SomeClass) with some method which receives Serializable object. The signature is following:
public void someMethod(Serializable serializableObject){
...
}
And I need to call this method from another class, but I should provide it with some List as fact parameter. There are two different approaches
1. Serializable
private SomeClass someClass;
public void doSomething() {
List<String> al = new ArrayList<String>();
al.add("text");
someClass.someMethod((Serializable)al);
}
2. ArrayList
private SomeClass someClass;
public void doSomething() {
ArrayList<String> al = new ArrayList<String>();
al.add("text");
someClass.someMethod(al);
}
Benefit of the first example is that it adheres to the java’s best practices which says: use interface instead of concrete realization for reference type and any programmer while reading that source will understand that we don't need special behavior of the ArrayList. And the only place we need it's serializable behavior we are adding this behavior by casting it to the Serializable interface. And programmer can simply change this current realization of the List to some other serializable realization, for example, LinkedList, without any side affect on this element because we use interface List as it`s reference type.
Benefit of the second example is that we refer to ArrayList as to class which have not only List behavior but also Serializable behavior. So if someone looked at this code and tried to change ArrayList to List he would receive a compile time error which would reduce time for programmer to understand what is going on there
UPDATE: we can't change someMethod signature. It came from a third-party company and we use it not only for Serializable Lists but also for Strings, Integers and some other Serializable objects
You should use an interface when all you need is the methods an interface provides. (this is most cases) However, if you need more than one interface, you can use generics, but the simplest approach is to use the concrete type.
It's better to define ArrayList because this combines two interfaces - List + Serializable. You need both of them in one place.
It doesn't matter that much, but not that using interfaces should be applied more strictly for return types, and less strictly for local variables.
I would change the signature of the someMethod so that it reflects what it requires from the invoker of the method:
public class SomeClass {
public <T extends List<? extends Serializable> & Serializable> void someMethod(T t) {
}
public static void main(String[] args) {
SomeClass test = new SomeClass();
test.someMethod(new ArrayList<String>()); //Works
test.someMethod(new ArrayList<Image>()); //Compile time error, Image is not Serializable
List<String> l = null;
test.someMethod(l); //Compile time error
}
}
The signature of someMethod now says that you must invoke it with something that is a List, and that is Serializable, and contains elements that are Serializable
In this case, I would just use List, and not worry that the compiler cannot guarantee that your object is serializable (it most likely will be anyway, if you've done things right elsewhere).
Note that methods of the following type (which accept a Serializable parameter) provide a false sense of security, because the compiler can never guarantee that the entire object graph which needs to be serialized will actually be serializable.
public void write(Serializable s);
Consider an ArrayList (serializable) which contains non-serializable objects. The signature may as well just be:
public void write(Object o);
And then you don't have to worry about all the extraneous casting.
Also consider that, although you cannot change the signature of the API you are using, you can very easily create a wrapper API which has a different signature.
1 is generally the right thing to do. However in this case, my opinion would to be bend that and declare it as ArrayList<>. This avoids the cast and guarantees that someone can't change the implementation of the List to one that isn't Serializable.
You can't do (1) because you're not free to change the List implementation type arbitrarily, which is the whole idea of doing that. You can only use a List implementation that implements Serializable. So you may as well express that in the code.