Java Reflection with Annotation Class not working - java

I'm pretty experienced with Java, however a novice to using Reflection and Annotation classes, which I'm trying to learn for fun. To get some practice, I made an Identifiable class which is designed to add several helpful methods to any class it inherits.
Here is the full class:
abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
#Retention(RetentionPolicy.RUNTIME)
public #interface Identifier { }
private static Method getMethodAnnotatedWith(final Class<?> type) {
return Arrays.stream(type.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(Identifier.class))
.findFirst()
.orElse(null);
}
private K id;
#SuppressWarnings("unchecked")
public Identifiable(Class<T> clazz) {
var m = getMethodAnnotatedWith(clazz);
if (m == null) throw new IllegalArgumentException(
clazz.toString() + " does not have a method annotated by #Identifier"
);
try {
id = (K) m.invoke(this);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int compareTo(#NotNull Identifiable<T, K> i) {
return id.compareTo(i.id);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<?, ?> that = (Identifiable<?, ?>) o;
return id == that.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
And here is how I am trying to design it to work:
class Foo extends Identifiable<Foo, Integer> {
private final int i;
Foo(int i) {
super(Foo.class);
this.i = i;
}
#Identifier
int getI() {
return i;
}
}
However, id is always 0 for some reason, so I'm not sure if it's a problem with my Identifier annotation class or the way I'm using reflection. I'm pretty sure it's the latter since while debugging, I found that it is able to access the method with the annotation. Any help would be appreciated, thanks!

Don't call the annotated method during construction.
If the identifier value is immutable (final), just pass the value to the super constructor.
public Identifiable(K id) {
this.id = id;
}
Foo(int i) {
super(i);
this.i = i;
}
If the identifier value is mutable, you need to change the logic to invoke the method when you need the value, not cache the value during construction.
abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
#Retention(RetentionPolicy.RUNTIME)
public #interface Identifier {/**/}
private Method idGetter;
protected Identifiable(Class<T> type) {
this.idGetter = Arrays.stream(type.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(Identifier.class))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(type.getName() + " does not have a method annotated by #Identifier"));
}
#SuppressWarnings("unchecked")
private final K getIdentifiableKey() {
try {
return (K) this.idGetter.invoke(this);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
#Override
public int compareTo(Identifiable<T, K> that) {
return this.getIdentifiableKey().compareTo(that.getIdentifiableKey());
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<?, ?> that = (Identifiable<?, ?>) o;
return this.getIdentifiableKey().equals(that.getIdentifiableKey()); // Call equals(), don't use ==
}
#Override
public int hashCode() {
return Objects.hash(this.getIdentifiableKey());
}
}
Alternatively, use a functional interface and supply it with a method reference.
abstract class Identifiable<T extends Identifiable<T, K>, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
private Function<T, K> idGetter;
protected Identifiable(Function<T, K> idGetter) {
this.idGetter = Objects.requireNonNull(idGetter);
}
#Override
#SuppressWarnings("unchecked")
public int compareTo(Identifiable<T, K> that) {
return this.idGetter.apply((T) this).compareTo(that.idGetter.apply((T) that));
}
#Override
#SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<T, K> that = (Identifiable<T, K>) o;
return this.idGetter.apply((T) this).equals(that.idGetter.apply((T) that));
}
#Override
#SuppressWarnings("unchecked")
public int hashCode() {
return Objects.hash(this.idGetter.apply((T) this));
}
}
class Foo extends Identifiable<Foo, Integer> {
private final int i;
Foo(int i) {
super(Foo::getI);
this.i = i;
}
int getI() {
return i;
}
}

Related

How to make custom Tuple class generic?

I'm trying to make a generic tuple class. It stores its elements as an ArrayList. Of course, this class should override hashcode and equals methods.
How could I make hashcode method for this class? You see, in the code, I am having trouble.
Also, for the equals method, why does the compiler force me to use the '?'. Why couldn't I just use the T?
public static class Tuple<T> {
ArrayList<T> tuple = new ArrayList<>();
public Tuple(ArrayList<T> items) {
for (T item : items) {
tuple.add(item);
}
}
#Override
public int hashCode() {
T sum = ???;
for (T item : tuple) {
sum += item.hashCode();
}
return sum;
}
#Override
public boolean equals(Object o) {
if (o instanceof Tuple<?>) {
Tuple<?> tup= (Tuple<?>) o;
if (tup.tuple.size() != this.tuple.size()) {
return false;
}
for (int i = 0; i < this.tuple.size(); i++) {
if (this.tuple.get(i) != tup.tuple.get(i)) {
return false;
}
}
return true;
} else {
return false;
}
}
}
As mentioned in the comments, we should delegate the hashCode and the equals methods to the ArrayList<T> tuple instance variable. For the hashCode it's trivial. For the equals it's just a little more complicated than that because we don't want our custom Tuple to be equals with an ArrayList. So here it is:
public class Tuple<T> {
// I made this private because I'm pedantric ;)
private final ArrayList<T> tuple = new ArrayList<>();
// this does the same as your code, it's just easier to read
public Tuple(ArrayList<T> items) {
tuple.addAll(items);
}
#Override
public int hashCode() {
return tuple.hashCode();
}
// generated by eclipse
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tuple other = (Tuple) obj;
if (tuple == null) {
if (other.tuple != null)
return false;
} else if (!tuple.equals(other.tuple))
return false;
return true;
}
}
If you want to deal with the case when the tuple can be null, then you can use a slightly more complex hashCode:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((tuple == null) ? 0 : tuple.hashCode());
return tuple.hashCode();
}
In general, I don't like to write these methods myself. Usually, I make my IDE to generate the stuff. All I need to take care of is to re-generate it when I add new fields. Apache HashCodeBuilder and EqualsBuilder are also great alternatives.

Get Class of a concrete Map

Given 2 Class objects how can I get the Class object of a Map?
For example, assume I have:
Class keyClass = Long.class;
Class valueClass = String.class;
How can I get the Class object ofMap<Long,String>?
There is no such class of Map<Long, String>. What you want is Map.class. (or HashMap.class, etc)
Map<String, Integer> map1 = new HashMap<>();
Map<Long, String> map2 = new HashMap<>();
System.out.println(map1.getClass().equals(map2.getClass()));
The result is true.
Map<Long, String> is not a class, but it is a type, ParameterizedType to be exact, sadly java code for constructing them is private, but they are 2 ways to get it, more dynamic way is to implement that interface:
final class ParameterizedTypeImpl implements ParameterizedType {
private final Type[] actualTypeArguments;
private final Class rawType;
#Nullable private final Type ownerType;
ParameterizedTypeImpl(Class rawType, Type[] actualTypeArguments, #Nullable Type ownerType) {
this.actualTypeArguments = actualTypeArguments.clone();
this.rawType = rawType;
if ((ownerType != null) || (rawType.getDeclaringClass() == null)) {
this.ownerType = ownerType;
}
else {
Class declaringClass = rawType.getDeclaringClass();
if (Modifier.isStatic(rawType.getModifiers())) {
this.ownerType = declaringClass;
}
else {
TypeVariable[] typeParameters = declaringClass.getTypeParameters();
if (typeParameters.length == 0) {
this.ownerType = declaringClass;
}
else {
this.ownerType = new ParameterizedTypeImpl(declaringClass, typeParameters, null);
}
}
}
}
#Override
public Type[] getActualTypeArguments() { return this.actualTypeArguments.clone(); }
#Override
public Class getRawType() { return this.rawType; }
#Nullable #Override
public Type getOwnerType() { return this.ownerType; }
#Override public boolean equals(Object o) {
if (o instanceof ParameterizedType) {
ParameterizedType that = (ParameterizedType) o;
if (this == that) return true;
Type thatOwner = that.getOwnerType();
Type thatRawType = that.getRawType();
return Objects.equals(this.ownerType, thatOwner) && Objects.equals(this.rawType, thatRawType) && Arrays.equals(this.actualTypeArguments, that.getActualTypeArguments());
}
return false;
}
#Override
public int hashCode() {
return Arrays.hashCode(this.actualTypeArguments) ^ Objects.hashCode(this.ownerType) ^ Objects.hashCode(this.rawType);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder(256);
if (this.ownerType != null) {
sb.append(this.ownerType.getTypeName());
sb.append("$");
if (this.ownerType instanceof ParameterizedTypeImpl) {
sb.append(this.rawType.getName().replace(((ParameterizedTypeImpl) this.ownerType).rawType.getName() + "$", ""));
}
else {
sb.append(this.rawType.getSimpleName());
}
}
else {
sb.append(this.rawType.getName());
}
StringJoiner joiner = new StringJoiner(", ", "<", ">");
joiner.setEmptyValue("");
for (Type type : this.actualTypeArguments) {
joiner.add(type.getTypeName());
}
sb.append(joiner.toString());
return sb.toString();
}
}
And then you can just do new ParameterizedTypeImpl(Map.class, new Type[]{String.class, Long.class}, null) note that it would be a good practice to make this class not visible to others and just create some factory methods.
Other less dynamic way is to use type tokens like in gson:
public class TypeToken<T> {
final Type type;
protected TypeToken() {
this.type = this.getClass().getGenericSuperclass();
}
public final Type getType() { return this.type; }
#Override public final int hashCode() { return this.type.hashCode(); }
#Override public final boolean equals(Object o) { return (o instanceof TypeToken<?>) && this.type.equals(((TypeToken<?>) o).type); }
#Override public final String toString() { return this.type.toString(); }
}
and then new TypeToken<Map<String, Long>>{}.getType(); - types needs to be provided at compile time.
There should be some libraries that provide both this methods, but I don't know any right now as I needed to make my own to also support parsing from string.

Unique classes in generic list

I have a generic class with a generic list in it. I want to ensure that the generic list only contains unique classes.
What I have done so far is to compare the class names with reflection (getClass()). But I think that's not a clean solution. Are there any better practices to check?
public class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
public void add(T t) {
final boolean[] classInMembers = {false};
members.forEach(member -> {
if (member.getClass().getName().equals(t.getClass().getName())) {
classInMembers[0] = true;
}
});
if (!classInMembers[0]) {
members.add(t);
}
}
public interface MyInterface {
void doSomething(String text);
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1());
myGenericClass.add(new Performer2());
myGenericClass.add(new Performer3());
myGenericClass.add(new Performer3()); // should not be inserted!
}
private static class Performer1 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 1!";
}
}
private static class Performer2 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 2!";
}
}
private static class Performer3 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 3!";
}
}
}
You could subclass a java.util.Set interface implementation. It will likely be easiest to subclass java.util.AbstractSet.
By default 'Set' will compare objects by their .equals() method - In your case, this is not sufficient. You will need to override the contains method to ensure that only instances of a unique class are added.
In your overrideen contains, it's probably the same / easier to compare class instances rather than their stringified package name
I.e. use a.getClass() == b.getClass(), rather than a.getClass().getName()
Don't use a List, use a java.util.Set instead.
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
If the iteration order is important or if you want to use a custom Comparator, the TreeSet implementation can be used:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
Example of a Set using a Comparator:
class MyComparator implements Comparator<Object> {
#Override
public int compare(Object e1, Object e2) {
if (e1.getClass() == e2.getClass())
return 0;
//if you wish to have some extra sort order
return e1.getClass().getName().compareTo(e2.getClass().getName());
}
}
. . .
Set mySet = new TreeSet<Object>(new MyComparator());
mySet.add(new Object());
mySet.add(new Object());//same class already in set
mySet.add("wtf");
//mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check
Why so complicated?
public class Main {
public static void main(String[] args) {
final Class<?> helloClass = "Hello".getClass();
final Class<?> worldClass = "World".getClass();
final Class<?> intClass = Integer.class;
System.out.println(helloClass.equals(worldClass)); // -> true
System.out.println(helloClass.equals(intClass)); // -> false
}
}
You could maintain a roster of members in a Set.
public static class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
// Add this.
private Set<Class<?>> roster = new HashSet<>();
public void add(T t) {
if (!roster.contains(t.getClass())) {
members.add(t);
roster.add(t.getClass());
}
}
private void soundOff() {
for (T t : members) {
t.doSomething();
}
}
public interface MyInterface {
void doSomething();
}
}
private static class Performer implements MyGenericClass.MyInterface {
final int n;
public Performer(int n) {
this.n = n;
}
#Override
public void doSomething() {
System.out.println("Hi, I am a " + this.getClass().getSimpleName() + "(" + n + ")");
}
}
private static class Performer1 extends Performer {
public Performer1(int n) {
super(n);
}
}
private static class Performer2 extends Performer {
public Performer2(int n) {
super(n);
}
}
private static class Performer3 extends Performer {
public Performer3(int n) {
super(n);
}
}
public void test() {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1(1));
myGenericClass.add(new Performer2(2));
myGenericClass.add(new Performer3(3));
myGenericClass.add(new Performer3(4)); // should not be inserted!
myGenericClass.soundOff();
}
You could implement a Wrapper which provides the necessary comparison and add the wrapped instance to the set. This way you don't have to override equals and hashcode in your concrete Performer classes and you don't have to subclass a concrete Set implementation (which you are coupled to. When you subclass a HashSet, you have to use that concrete class. But what if you want to use a LinkedHashSet at some point? You have to override LinkedHashSet as well) , which may be fragile since you have to make sure that the overridden method is consistent with the rest of the class.
class MyGenericClass<T extends MyInterface> {
private Set<ClassCompareWrapper<T>> members = new HashSet<>();
public void add(T t) {
members.add(new ClassCompareWrapper<T>(t));
}
}
class ClassCompareWrapper<T> {
T t;
public ClassCompareWrapper(T t) {
this.t = t;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClassCompareWrapper))
return false;
ClassCompareWrapper<?> that = (ClassCompareWrapper<?>) o;
return Objects.equals(t.getClass(), that.t.getClass());
}
#Override
public int hashCode() {
return Objects.hash(t.getClass());
}
#Override
public String toString() {
return "Wrapper{" +
"t=" + t +
'}';
}
}
Here are a few other ideas.
Using streams:
public void add(T t) {
if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) {
members.add(t);
}
}
Using AbstractSet and HashMap:
class ClassSet<E> extends AbstractSet<E> {
private final Map<Class<?>, E> map = new HashMap<>();
#Override
public boolean add(E e) {
// this can be
// return map.putIfAbsent(e.getClass(), e) != null;
// in Java 8
Class<?> clazz = e.getClass();
if (map.containsKey(clazz)) {
return false;
} else {
map.put(clazz, e);
return true;
}
}
#Override
public boolean remove(Object o) {
return map.remove(o.getClass()) != null;
}
#Override
public boolean contains(Object o) {
return map.containsKey(o.getClass());
}
#Override
public int size() {
return map.size();
}
#Override
public Iterator<E> iterator() {
return map.values().iterator();
}
}
A HashMap could also be used without wrapping it in a Set. The Set interface is defined around equals and hashCode, so any implementation which deviates from this is technically non-contractual. Additionally, you might want to use LinkedHashMap if the values are iterated often.

Generic implementation of Aggregator

I am creating a class CommonAggregator which can aggregate any object which is Aggregatable.
public interface Aggregatable<T> {
public void addFieldValue(T t, AggregationField<T> field);
}
public class Charges implements Aggregatable<Charges>{
//privat fields
//Getter and Setters
#Override
public void addFieldValue(Charges Charges, AggregationField<Charges> field){
if(ChargeDetailAggregationField.TYPE1 == field){
Type1 += Charges.getType1();
}else if(ChargeDetailAggregationField.TYPE2 == field){
Type2 += Charges.getType2();
}else if(ChargeDetailAggregationField.TYPE3 == field){
Type3 += Charges.getType3();
}
}
}
public class CommonAggregator<T extends Aggregatable<T>> {
private static enum AggregationOperation {
SUM, MAX, MIN, AVG;
}
private AggregationField<T>[] fieldsForSum;
private AggregationField<T>[] fieldsForMax;
private AggregationField<T>[] fieldsForMin;
//private AggregationField groupByField = null;
public CommonAggregator<T> sum(AggregationField<T>... fields){
this.fieldsForSum = fields;
return this;
}
public CommonAggregator<T> max(AggregationField<T>... fields){
this.fieldsForMax = fields;
return this;
}
public CommonAggregator<T> min(AggregationField<T>... fields){
this.fieldsForMin = fields;
return this;
}
private <T> void performOperation(AggregationOperation op,AggregatedResponse<T> aggregatedDetails,List<T> aggregatables,AggregationField<T>... fields){
Aggregatable<T> aggregatedResponse = (Aggregatable<T>) getNewInstance();
T response = null;
for(AggregationField<T> field:fields){
if(op == AggregationOperation.MAX){
response = max(field,aggregatables);//Compilation Err
}else if(op == AggregationOperation.MIN){
response = min(field,aggregatables);//Compilation Err
}else if(op == AggregationOperation.SUM){
response = sum(field,aggregatables);//Compilation Err
}
aggregatedResponse.setFieldValue(response, field);
if(op == AggregationOperation.MAX){
aggregatedDetails.setMax(aggregatedResponse);
}else if(op == AggregationOperation.MIN){
aggregatedDetails.setMin(aggregatedResponse);
}else if(op == AggregationOperation.SUM){
aggregatedDetails.setSum(aggregatedResponse);
}
}
}
private T max(AggregationField<T> field,List<T> aggregatables){
CommonComparator<T> comparator = new CommonComparator<T>(SortOrder.ASCENDING, field);
return Collections.max(aggregatables, comparator);
}
private T min(AggregationField<T> field,List<T> aggregatables){
CommonComparator<T> comparator = new CommonComparator<T>(SortOrder.ASCENDING, field);
return Collections.min(aggregatables, comparator);
}
private T sum(AggregationField<T> field,List<T> listOfAggregatables){
T aggregatable = listOfAggregatables.get(0);
for(T response :listOfAggregatables.subList(1, listOfAggregatables.size())){
aggregatable.addFieldValue(response, field);
}
return aggregatable;
}
public AggregatedResponse<T> aggregate(List<T> aggregatables){
AggregatedResponse<T> aggregatedDetails = new AggregatedResponse<T>();
if(fieldsForMax != null)
performOperation(AggregationOperation.MAX,aggregatedDetails,aggregatables,fieldsForMax);
if(fieldsForMin != null)
performOperation(AggregationOperation.MIN,aggregatedDetails,aggregatables,fieldsForMin);
if(fieldsForSum != null)
performOperation(AggregationOperation.SUM,aggregatedDetails,aggregatables,fieldsForSum);
return aggregatedDetails;
}
public <E> Map<E,List<T>> groupBy(AggregationField<T> fieldName, List<T> listOfAggregatable){
Map<E,List<T>> groupedList = new HashMap<E,List<T>>();
for(T t:listOfAggregatable){
List<T> subList = null;
E fieldValue = (E)t.getFieldValue(fieldName);
if((subList = groupedList.get(fieldValue)) != null){
subList.add(t);
}else{
subList = new ArrayList<T>();
subList.add(t);
groupedList.put(fieldValue,subList);
}
}
return groupedList;
}
public <E> Map<E,AggregatedResponse<T>> groupByWithAggregation(AggregationField<T> fieldName, List<T> listOfAggregatable){
//groupByField = fieldName;
Map<E, List<T>> groupedByList = groupBy(fieldName, listOfAggregatable);
Map<E,AggregatedResponse<T>> mapOfAggregatedDetails = new HashMap<E, AggregatedResponse<T>>();
for(E key : groupedByList.keySet()){
mapOfAggregatedDetails.put(key, aggregate(groupedByList.get(key)));
}
return mapOfAggregatedDetails;
}
:
}
This is not the complete code.
Here, AggregationField tells which field of Aggregatable class has to be aggregated.
Problem:
I have facing followinf error when calling max(), min(), and sum() in performOperation()
The method max(AggregationField< T>, List< T>) in the type CommonAggregator< T> is not applicable for the arguments (AggregationField< T>, List< T>)
Edit: I have modified the original code and question after #Mikhail suggestion.
I am not good in generics. And I guess I am doing something wrong in generics only.
public interface AggregationField <T>
{
// ...
}
public interface Aggregatable <T>
{
public void addFieldValue (T t, AggregationField <T> field);
}
public class Charges implements Aggregatable <Charges>
{
#Override
public void addFieldValue (
Charges Charges, AggregationField <Charges> field)
{
// ...
}
}
public class CommonAggregator <T extends Aggregatable <T>> {
private T sum (
AggregationField <T> field,
List <? extends T> listOfAggregatables)
{
T aggregatable = listOfAggregatables.get(0);
for (T response: listOfAggregatables.subList (1, listOfAggregatables.size())){
aggregatable.addFieldValue(response, field);
}
return aggregatable;
}
}

Problems with add/remove/equals/string-method in MultiSet-class

This is my class:
public class MultiSet<E> extends AbstractCollection<E>
{
private int size = 0;
private Map<E, Integer> values = new HashMap<E, Integer>();
public MultiSet()
{
}
public MultiSet(Collection<E> c)
{
addAll(c);
}
#Override
public boolean add(E o)
{
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o)
{
throw new UnsupportedOperationException();
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
private Iterator<E> iterator = values.keySet().iterator();
private int remaining = 0;
private E current = null;
public boolean hasNext()
{
return remaining > 0 || iterator.hasNext();
}
public E next()
{
if (remaining == 0)
{
remaining = values.get(current);
}
remaining--;
return current;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
public boolean equals(Object object)
{
if (this == object) return true;
if (this == null) return false;
if (this.getClass() != object.getClass()) return false;
MultiSet<E> o = (MultiSet<E>) object;
return o.values.equals(values);
}
public int hashCode()
{
return values.hashCode()*163 + new Integer(size).hashCode()*389;
}
public String toString()
{
String res = "";
for (E e : values.keySet());
//res = ???;
return getClass().getName() + res;
}
public int size()
{
return size;
}
}
So basically, i need to implement my add/remove-methods correctly, to add or remove elements to/from the Set.
To me, it seems like my equals is correct, but Eclipse says that in the line:
MultiSet<E> o = (MultiSet<E>) object;
there is an unchecked cast from object to Multiset<E>
Any thoughts?
Also, in my toString method, i'm not 100% sure how to define "res"?
Thanks,
// Chris
use this instead:
MultiSet<?> o = (MultiSet<?>) object;
this is necessary due to how generics are implemented in java.

Categories