I'm trying to implement a Collection interface with Iterator as inner class. An ArrayCollection class that implements collection has a generic array (is it a correct way to say that class members are generic?).
a screenshot from IDE
public class ArrayCollection<T> implements Collection<T> {
private T[] m = (T[])new Object[10];
However when I'm implementing method next() for an Iterator interface I keep getting an Incompatible types error. However if make an ArrayIterator a non-generic class compiler doesn't have problems with array typecasting anymore.
an error screenshot from IDE
private class ArrayIterator<T> implements Iterator<T> {
private int cursor = 0;
#Override
public boolean hasNext() {
return this.cursor >= ArrayCollection.this.size();
}
#Override
public T next() {
return ArrayCollection.this.m[cursor++];
}
}
So I have few questions:
How come the compiler can define the T[] m array type if I make an ArrayIterator non-generic?
Is it only the inner classes that implement/extend generic interfaces/classes can be non-generic?
You named your type variables similarly, ArrayIterator<T> has a different T than ArrayCollection<T>'s T is.
You can just remove the <T> from ArrayIterator (since it is a non-static inner class) and just have Iterator use the T from the parent class:
private class ArrayIterator implements Iterator<T> {
This will fix the compile issue and your code.
In the second example, you are dealing with two different type variables. The outer class and the inner class each define a variable T, but they are not the same. There are several ways to solve this, one is to remove the T from the inner class declaration:
private class ArrayIterator implements Iterator<T> {
Now you are referencing the outer T only, without introducing a separate inner T
However, I would personally prefer to make the inner class static, in which case you can't use the outer type's variables. In this case you'd write
private static class ArrayIterator<T> implements Iterator<T> {
If you do this, you need to pass the type variable from the outer type to the inner:
return new ArrayIterator<T>();
The main difference is that a class that defines a type parameter shadows any existing type parameter of the same name, which leads to that strange error message.
Finally let me add that it's usually not a good idea to implement a Collection from scratch. Instead, you might want to extend AbstractCollection or AbstractList. This will let you focus on your core algorithm, but provide you with all the boilerplate methods for free.
Related
I've seen a number of similar questions, but I don't think any were quite isomorphic, and none quite answered my question.
Suppose there are two interfaces, Tree and Named. Suppose further that I am given a method whose signature is
public <T extends Tree & Named> T getNamedTree();
How can I save the returned value to a variable, while still retaining the information that it implements both Tree and Named? I can't find a way of declaring a variable like
public <T extends Tree & Named> T mNamedTree;
and trying to cast it to an interface extending Tree and Named results in a class cast exception.
Assuming there is no third interface inheriting both Named and Tree, you cannot retain information about both interfaces statically. The compiler will require you to do a cast for one or the other, or for both:
Object namedTree = getNamedTree();
Tree asTree = (Tree)namedTree;
Named asNamed = (Named)namedTree;
Both casts should succeed.
If you have influence on the design of the API for the class, ask the authors to introduce an interface combining both Named and Tree, and returning an instance of that interface instead.
One possible solution would be to create another interface that extends both Tree and Named, and simply store that as the variable:
interface NamedTree extends Tree, Named {
}
public NamedTree namedTree;
public NamedTree getNamedTree();
What scope does the variable has to have?
There is three possibilities here.
A) the variable is just a local variable. In that case you nearly have already the answer... you just need to declare a type-parameter for the enclosing method for that type:
interface ItfA { Number propA(); };
interface ItfB { Number propB(); };
class Main {
private <T extends ItfA & ItfB> T getT() {
return null;
}
private <TT extends ItfA & ItfB> void doStuffWithT() {
TT theT = getT();
System.err.println(theT.propA());
System.err.println(theT.propB());
}
}
B) The scope is the live of an object and in that case is a member field.
The obvious answer is to make the class generic and the type-parameter would
have the same & constraint:
interface ItfA { Number propA(); };
interface ItfB { Number propB(); };
class Main<T extends ItfA & ItfB> {
T theT;
public void setT(T newT) {
theT = newT;
}
public void doStuffWithT() {
System.err.println(theT.propA());
System.err.println(theT.propB());
}
}
C) The scope is the live of the program, then the variable is a static class member. Here you don't have a generics solution.
C.1) Obviously if the class of the values that you are going to handle is known you would just use that class as the field type.
C.2) If not, you could constraint the code to handle only classes that implement an interface that extends ItfA and ItfB. That interface, say ItfAB. Would be to field type.
C.3) Now, what about not imposing that constraint? What about allow the code to handle objects from any class that implement those interfaces?
Unfortunately there is no a clean-cut solution to that:
C.3.a) You could either type the field Object and provide methods to access it as an ItfA or as a ItfB (basically hiding the casting).
C.3.b) Or, instead of holding directly a reference to the object, you use a proxy object that implements those interfaces and delegates calls to those interfaces methods to the original "T" typed value. The class for that proxy could itself be a generic accepting an arbitrary <T extends ItfA & ItfB> value (similar to the B. example above).
I've seen a few questions about the sort for collections having errors in Java. The error I am showing is this:
The method sort(List<T>) in the type Collections is not applicable for the arguments (ArrayList<Time>)
I have imported java.util.Collections and ArrayList. I also imported the class I am calling from. Here is my code:
In the class being called from:
private ArrayList<Time> times;
...
public ArrayList<Time> getTimes() {
return this.times;
}
In the class I am calling the array list to:
public class TimeTUI {
private Scanner scan;
private TimeManager timeManager;
...
private ArrayList<Time> getSortedTimes() {
ArrayList<Time> sortedTimes = this.timeManager.getTimes();
Collections.sort(sortedTimes);
return sortedTimes;
}
The error is appearing on the line showing:
Collections.sort(sortedTimes);
The class Time has to be a Comparable.
Collections.sort(List) expects that the class T implements Comparable interface. If you have used many of the inbuilt classes, you wouldn't find problem, but for the custom classes sort doesn't know how to sort them. So, by implementing Comparable interface, you give definition to a method compareTo.
public class Time implements Comparable {
public int compareTo(Object o) {
// provide your logic of how to sort Time objects.
}
}
Your class type in the List or ArrayList must implement the Interface comparable and override properly the compareTo(...) method,
Is you break this contract and dont implement the interface. the Class Collections has not a valid criteria/rule to compare/sort your list, and therefore your compiler will complain...
I don't think that it is the ArrayList that is the issue here. For example:
ArrayList<String> names = new ArrayList<>();
...
Collections.sort(names);
works just fine.
The content of the list must be comparable so that the sort can work. In this case the Time class and any sub-type must implement Comparable.
I just wonder what happens after I execute:
List list = Collections.<String>emptyList();
Here is Collections code:
public static final List EMPTY_LIST = new Collections.EmptyList(null);
public static final <T> List<T> emptyList() {
return EMPTY_LIST;
}
private static class EmptyList<E> extends AbstractList<E>
implements RandomAccess, Serializable {
private EmptyList() {
}
//...
}
How is it possible to call EmptyList(null) while EmptyList doesn't have constructor with arguments.
Please, explain me the process how does Collection's method generic type T becomes EmptyList's generic type E?
I just wonder what happens after I execute
Nothing happens because everything about Generics and type parameters goes on at compile time.
How is it possible to call EmptyList(null) while EmptyList doesn't have constructor with arguments
It is not possible, but no JDK code I saw creates the empty list by passing null to the constructor. Don't trust decompiler code.
explain me the process how does Collection's method generic type T becomes EmptyList's generic type E?
T is a type variable capturing the type inferred at each call site of emptyList(). This variable is "passed" (during static type analysis) to emptyList so that the type of the returned value is List<T>. E is the name of the type parameter of EmptyList and this type parameter assumes the value of T in that particular type instantiation. This is very similar to how an ArrayList<E>'s E becomes String when you say new ArrayList<String>().
In addition to #MarkoTopolnik answer I can explain why decompiler shows that null is passed into constructor parameters.
When you create private class with no explicit constructor, its default no-arg constructor is generated inside the bytecode as private:
private static class EmptyList<E> {
private EmptyList() {
}
...
}
This way the class cannot be instantiated from other classes (JVM will not permit this). But it does instantiated in Collections class:
public static final List EMPTY_LIST = new EmptyList<>();
From the JVM point of view there's no thing like "nested class". The EmptyList class is just another class (named Collections$EmptyList) in the same package. If javac compiler generate the direct call to the private constructor, then JVM will just throw an exception during the Collections class initialization. To fix this problem javac compiler introduces one more additional constructor with package-private access:
private static class EmptyList<E> {
private EmptyList() {
}
EmptyList(Collections$1 ignore) {
this();
}
...
}
This artificial constructor has one parameter just to distinguish from the existing constructor. Also to avoid possible arguments clash an additional artificial class Collections$1 is generated! The purpose of this class is just to be the parameter of such artificial constructors. It's never instantiated and even initialized.
So finally you can instantiate the EmptyList from another class (within java.util package) calling this new constructor:
public static final List EMPTY_LIST = new EmptyList<>((Collections$1)null);
This is how it looks in the bytecode. Seems that decompiler is not smart enough to detect this situation and remove the artificial constructor parameter in the output (actually it should be quite simple).
Your decompiled code:
public static final List EMPTY_LIST = new Collections.EmptyList(null);
Actual code
#SuppressWarnings("unchecked")
public static final List EMPTY_LIST = new EmptyList<>();
In the actual code, there is no parameter to the constructor.
Your decompiled code:
public static final <T> List<T> emptyList() {
return EMPTY_LIST;
}
Actual code:
#SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
So, as for how does the type T becomes E? By cheating. EMPTY_LIST is a raw type, and they explicitly cast it into List<T> and suppress the warnings both on creation of the original EMPTY_LIST and on this explicit cast.
In reality, this is pretty safe exactly because the list is empty and will stay empty.
Moral of the story: don't trust decompilers, especially not when it comes to generics, because with type erasure, it's really hard for a decompiler to know what actually happened in the original source code. If you want to look at the source of various open-source Java projects, including OpenJDK, use the useful (though slow) GrepCode website.
So I have a class that implements Comparable (I have a dummy method here for brevity)
public class MarkovEntry<T extends Chainable> implements Comparable<MarkovEntry<T>>
{
// Compare two rows by ID
public int compareTo(MarkovEntry<T> e)
{
return 0;
}
}
And a method in another class that takes a Comparable (once again, dummy method)
public class ArrayOps
{
public static int binSearch(ArrayList<Comparable> list, Comparable c)
{
return 0;
}
}
Yet when I try to call my method as follows
int index = ArrayOps.binSearch(entries, newEntry);
Where entries is an ArrayList of MarkovEntry's and newEntry is a MarkovEntry, the compiler tells me
actual argument java.util.ArrayList<com.company.MarkovEntry<T>> cannot be converted
to java.util.ArrayList<java.lang.Comparable> by method invocation.
What is going on here? MarkovEntry specifically implements Comparable -- why doesn't the compiler recognize that?
My class Chainable implements Comparable as well, in case that has anything to do with it.
Generics are a little strange in that
ArrayList<SuperType>
is not actually a supertype of
ArrayList<SubType>
e.g. ArrayList<Number> is not a supertype of ArrayList<Integer>. This is because if such a relationship held you could substitute in an ArrayList<Number> for an ArrayList<Integer>, which would then allow operations that would have been illegal if you didn't make the replacement.
To be more specific, say you did this:
ArrayList<Number> list = new ArrayList<Integer>();
You'd then be able to put in a Double into list because to the compiler, list is an ArrayList<Number>! As you can see, this breaks the guarantees that generics should provide, so it isn't allowed.
What you're looking for is a generic method like this:
public static <T extends Comparable<? super T>> int binSearch(ArrayList<T> list)
Basically, you can generify methods the same way you can generify classes.
More info can be found here: http://docs.oracle.com/javase/tutorial/extra/generics/methods.html
So I've been reading through the Generics tutorial offered by Oracle here: http://docs.oracle.com/javase/tutorial/java/generics/
And I've tried running my own example to make sure I understand how to use Generics. I have the following code:
import java.util.*;
public class Generics {
class NaturalNumber {
private int i;
public NaturalNumber(int i) { this.i = i; }
}
class EvenNumber extends NaturalNumber {
public EvenNumber(int i) {
super(i);
}
}
public static void main(String[] args) {
Collection<? extends NaturalNumber> c = new ArrayList<>();
c.add(new EvenNumber(2)); //this line produces a compile time error
}
}
My goal is to be able to add any object which is a subtype of NaturalNumber to the Collection c. I'm not sure why this doesn't work and reading through Oracle's tutorial hasn't enlightened me either.
When you have ? extends NaturalNumber, the parameter could be some other subclass of NaturalNumber that is in no way related to EvenNumber. For instance,
Collection<? extends NaturalNumber> c = new ArrayList<OtherNaturalNumber>();
is valid if OtherNaturalNumber extends NaturalNumber.
Consequently, you are not able to add an EvenNumber instance to the list. You can just use this declaration:
Collection<NaturalNumber> c = new ArrayList<>();
which will allow you to add any NaturalNumber instance (including an EvenNumber).
On another note, you probably meant to make those nested classes static (or don't nest them at all).
First Collection<? extends NaturalNumber> should just be Collection<NaturalNumber>. Instances of EvenNumber (or any NaturalNumber or a subtype of NaturalNumber) can be put into the collection this way.
Essentially Collection<? extends NaturalNumber> says that the type of the type parameter to Collection extends NaturalNumber. So say that class OddNumber extends NaturalNumber, then the type of Collection's type parameter could be OddNumber which EvenNumber cannot be safely cast to.
However there is another compiler error. To be used in the static context or main(String[]) each of the inner classes NaturalNumber and EvenNumber need to have the modifier static placed on each class declaration.
Your problem is that you've told the compiler that the Collection's element type can be any type that extends NaturalNumber, but then you tried to insert an object into it. As far as the compiler knows, c is a Collection<OddNumber>, and you just added an EvenNumber!