Java Generics : Converting methods to a single method? - java

In my Spring Boot app, I have the following methods:
public class ContainsIntegerFilter implements CsvToBeanFilter {
private final int index;
private final List<Integer> values; // list types are different (Integer)
public boolean allowLine(String[] line) {
return values.contains(Integer.parseInt(line[index]));
}
}
public class ContainsStringFilter implements CsvToBeanFilter {
private final int index;
private final List<String> values; // list types are different (String)
public boolean allowLine(String[] line) {
return values.contains(line[index]);
}
}
And calling these methods like this:
final CsvToBeanFilter filter = new ContainsIntegerFilter(0, idList);
I want to merge these methods into a single method using generics. But I am not sure if the following approach is the most proper way? Or should I use an interface and call that interface instead of the merged method:
public abstract class ContainsFilter<T> implements CsvToBeanFilter {
private final int index;
private final List<T> values;
public boolean allowLine(T[] line) {
return values.contains(line[index]);
}
}
So, how can achieve this?

In first approach, you are always passing String[] array as method arguments (both for string and int).
In second approach, you are passing T[] array which will change based on type T.
If you pass can String[] array in one case & Integer[] array in another as method arguments, you can use first one. That keeps code clean.
Update:
Sample code for second approach:
public class ContainsFilter<T> {
private final int index = 1;
private List<? extends Object> values1 = List.of("test1", "test2");
private List<? extends Object> values2 = List.of(1, 2);
public void allowLine(T[] line) {
System.out.println(values1.contains(line[index]));
System.out.println(values2.contains(line[index]));
}
public static void main(String[] args) {
String[] line1 = new String[] {"test111", "test2"};
Integer[] line2 = new Integer[] {1, 2};
ContainsFilter<String> c1 = new ContainsFilter<String>();
c1.allowLine(line1);
ContainsFilter<Integer> c2 = new ContainsFilter<Integer>();
c2.allowLine(line2);
}
}

Related

Java Generics: Stream toArray()

Given a simple generic class:
private static class Container<T> {
private List<T> aList;
private T aValue;
private Container(List<T> aList, T aValue) {
this.aList = aList;
this.aValue = aValue;
}
}
Initialize a list of that class:
List<Container<?>> container = new ArrayList<>();
// Add some elements...
Not possible (The method toArray(IntFunction<A[]>) in the type Stream<List<capture#1-of ?>> is not applicable for the arguments (List<?>[])):
container.stream().map(value -> value.aList).toArray(new List<?>[0]);
Possible:
container.stream().map(value -> value.aList).collect(Collectors.toList()).toArray(new List<?>[0]);
Why?
Stream's toArray takes a IntFunction<A[]> - i.e. a function that accepts an int and returns an array.
You tried to pass an array to it.
It should be used as follows:
container.stream().map(value -> value.aList).toArray(List<?>[]::new)

Cannot cast Comparable to a class which implements it

I'm trying to create a genetic ArrayList of my class Team but I can't cast Comparable to T despite that T extends Comparable
(I tried extends Comparable without putting < T > and same problem is happening)
public class ArrayList<T extends Comparable>
{
static int MaxSize = 1003;//some random maximum size for our array lists
public int Size = 0;
public int Capacity = 0;
T[] MyList;
public ArrayList()//Default Constructor
{
this(MaxSize);
}
public ArrayList(int Capacity)
{
MyList = (T[]) new Comparable[Capacity]; // Casting
}
}
public class Team implements Comparable<Team>
{
public String Name;
public int Points;
public int GamesCount;
public int Wins;
public int Loses;
public int GoalDifference;//Goals Scored - Goals Against
public int GoalsScored;
public int GoalsAgainst;
public Team(String s)
{
Name = s;
Points = 0;
GamesCount = 0;
Wins = Loses = 0;
GoalDifference = GoalsAgainst = GoalsScored = 0;
}
}
public class Test
{
public static void main(String args[])
{
ArrayList<Team> Teams = new ArrayList<Team>(10);
for(int i = 0 ;i < 10;i++)
{
String TeamName = in.next();
Teams.MyList[i] = new Team(TeamName);//exception here
}
}
}
I am getting the following exception. Many thanks in advance for your help.
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to [LTeam;
at Test.main(Test.java:21)
That's obvious, just think about it for a second.
new Comparable[Capacity];
Here you're creating an array which will contain Comparable elements.
Then you're trying to downcast it to an array of Team, which means trying to use a more specific type, and which is not allowed (Upcasting and downcasting in Java), and as you see, throws a ClassCastException.
As your array will contain only types which extends Comparable, you can have a Comparable array inside your ArrayList implementation.
Comparable[] MyList;
Then you can initialize it without casting
MyList = new Comparable[Capacity];
And remember to implement the Comparable#compareTo method in your Team class, otherwise the code won't compile.
You asked for an example.
public class ArrayList<T extends Comparable> {
private static final int MAX_SIZE = 1000;
private final Comparable<T>[] list;
public ArrayList() {
this(MAX_SIZE);
}
public ArrayList(int capacity) {
list = new Comparable[capacity]; // Casting
}
public T get(final int index) {
return (T) list[index];
}
public void set(final int index, final T value) {
list[index] = value;
}
}
Usage is pretty simple
final ArrayList<Team> teamArrayList = new ArrayList<>(3);
teamArrayList.set(0, new Team("One"));
teamArrayList.set(1, new Team("Two"));
teamArrayList.set(2, new Team("Three"));
final String name = teamArrayList.get(0).Name;
As you extended the Comparable<T> interface, you need to implement it
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object
A basic example is
#Override
public int compareTo(final Team other) {
return name.compareTo(other.name);
}
In your code, T is Team.
Team IS-A Comparable. Hence, you can cast Team to Comparable.
But every Comparable is not a Team. Therefore, Comparable cannot be cast to Team.
watch this statement: MyList = (T[]) new Comparable[Capacity]; it initializes an array from Comparable interface and put it in to MyList field with type of Team[].
you can test it with:
{
MyList = (T[]) new Comparable[Capacity]; // Casting
System.out.println( MyList.getClass().toString());
}
it writes class [Ljava.lang.Comparable; in output... so in the first attempt to access this field from outside of class you will get ClassCastException even by calling length in test method like this:
public class Test
{
public static void main(String args[])
{
MArrayList<Team> Teams = new MArrayList<Team>(10);
int length = Teams.MyList.length; // this line throws ClassCastException
....
}
}
To solve your problem I suggest change your ArrayList class like this:
public class ArrayList<T extends Comparable<T>>
{
...
Comparable<T>[] MyList;
...
public ArrayList(int Capacity)
{
MyList = new Comparable[Capacity]; // Casting
}
}

I'm trying to create an arrayList that could accept both integers and array

How do I create a generic array list in Java that could accept both integers and array?
[123,[],112,[],10]
Both int (Integer wrapper) and array's common base class is Object. So create ArrayList using Object type.
ArrayList<Object> list= new ArrayList<Object>();
But this is not the right way to solve this problem and there is no use of Generics here to make run-time safety. So, re-design your program and allocate each type in seperate list or use any other appropriate Collection type.
Well, the fastest way might be create an auxiliar object that is composed by boths Integers and array letting you to use boths by equal
Create the array list at the Generalization level for all the objects you need in the List. In this case,i.e for int and array, it is java.lang.Object.
Here's a small test I ran:
public static void main(String[] args) {
List<Object> both = new ArrayList<Object>();
both.add(1);
both.add(args);
System.out.println(both);
}
Produces:
[1, [Ljava.lang.String;#1db9742]
As #AbtPst suggested, the most suitable solution would be to have a list of lists. This can be done in many different ways. Personally, I'd create a class with two constructors.
class NumericElement
{
private ArrayList<int> elements;
public NumericElement(int newElement)
{
elements = new ArrayList<int>();
elements.add(newElement);
}
public NumericElement(ArrayList<int> newElements)
{
elements = new ArrayList<int>(newElements); // copying array elements to the new array.
}
public ArrayList<int> getElements()
{
return elements;
}
}
Use a class which has an int and an array as its instance variables. Then create an ArrayList like
import java.util.ArrayList;
public class Hello {
public static void main(String[]args) {
ArrayList<Intarray> myArrayList = new ArrayList<Intarray>();
int[] arr = {3,4,5};
myArrayList.add(new Intarray(2,arr));
}
}
class Intarray {
private int numbers;
private int[] myArray;
public Intarray(int numbers, int[] myArray){
this.numbers = numbers;
this.myArray = myArray;
}
}
You can define a List class with a type-safe interface, hiding an unsafe List<Object> as an internal implementation detail.
This is more work than just using a List<Object> directly. If your list is used only by one class, just use a List<Object>, as an implementation detail of that one class. But if your list is exposed to access by more than just one class, consider using this type-safe approach.
First, define an interface that can represent either an int or a array of ints.
public interface IScalarOrArrayInt { }
And define a sub-interface for each possible element type:
public interface IScalarInt extends IScalarOrArrayInt {
public int getScalarInt();
}
public interface IArrayInt extends IScalarOrArrayInt {
public int[] getIntArray();
}
Then define your list class and its representation. It's interface can be a List<IScalarOrArrayInt>. The representation can be a List<Object>, so that you can put Integer and int[] objects into it directly, without wrapper objects.
public class ListOfScalarsAndArray extends AbstractList<IScalarOrArrayInt> {
private static List<Object> m_list = new ArrayList<Object>();
As noted in the AbstractList documentation, you'll want to define several methods to allow modifying your list. You can delegate them to the internal list, but wrap the return values.
#Override
public void add(int index, IScalarOrArrayInt element) {
m_list.add( index, element );
}
#Override
public IScalarOrArrayInt remove(int index) {
return wrap( m_list.remove( index ));
}
#Override
public IScalarOrArrayInt set(int index, IScalarOrArrayInt element) {
return wrap( m_list.set( index, element ));
}
For the convenience of callers, you can add some methods that accept an unwrapped int or int[]. For example:
public void add( int element ) {
m_list.add( element );
}
public void add( int[] element ) {
m_list.add( element );
}
To satisfy the standard List<> interface, you can wrap return values. Your class controls the internal list, so it alone controls the possible types of list members.
private IScalarOrArrayInt wrap( Object o ) {
if ( o instanceof Integer ) {
final int i = (Integer) o;
return new IScalarInt() {
#Override
public int getScalarInt() {
return i;
}
};
}
else {
assert( o instanceof int[] );
final int[] a = (int[]) o;
return new IArrayInt() {
#Override
public int[] getIntArray() {
return a;
}
};
}
}

How to Convert int to Generics Type T

I have a class with generic type.
It is nice and easy to add any type of object I want on Mylist using addNode( ) since I am using generics. However, if I want to add in (int) 5 while I am in the class. How am I able to do that? (See comment in below codes for example).
class MyList <T>
{
Node <T> head = null;
public void addNode(T element) {
head = new Node <T> (element);
}
//Other methods removed for simplicity
public void myMethod () {
MyList <T> newList = new MyList <T>();
//Below code generate error: required: T, found: int
newList.addNode(5); //How to add an int 5 here when it is expecting Type:T?
}
}
class Node <T>
{
T element;
public Node(T item)
{
element = item;
}
//Other methods removed for simplicity
}
class MyList<T> {
// ...
public void addNode(T element) {
// ...
}
public void myMethod() {
MyList<T> newList = new MyList<T>();
newList.addNode(5); // <- can't do that!
}
}
If you could add an integer there, the class wouldn't be generic anymore. It would defeat the purpose.
In methods outside this class that use MyList, you could do this:
void another() {
MyList<Integer> list = new MyList<Integer>();
list.addNode(5);
MyList<String> list2 = new MyList<String>();
list.addNode("hello");
}
UPDATE
What if I need to add both Integer and Nodes object into the list? Is there any way we can do it?
You could do this, but then, what's the template parameter is becoming pointless, and you are losing proper type checking features. Normally you shouldn't do this.
MyList<Object> list3 = new MyList<Object>();
list3.addNode(12);
list3.addNode("heya");

How to do custom order on java ENUMS?

I have Enum class as
public enum ServiceTypes {
Zero("zero", 6, true),
AVL("Avl", 1, true),
VPS("vps", 2, true),
CALCULATOR("calculator", 3, true),
SIMULATOR("Simulator", 4, true),
CONTRACT("Contract Output", 5, true),
ALL("all", 7, true);
private static final OrderComparator orderComparator = new OrderComparator();
private static final OrderComparator nameComparator = new OrderComparator();
#Nonnull
public static ServiceTypes[] getOrderedServiceTypes() {
Arrays.sort(values(), orderComparator);
return values();
}
#Nonnull
public static ServiceTypes[] getNamedServiceTypes() {
Arrays.sort(values(), nameComparator);
return values();
}
private static final class OrderComparator implements Comparator<ServiceTypes> {
#Override
public int compare(final ServiceTypes o1, final ServiceTypes o2) {
return o1.getOrder().compareTo(o2.getOrder());
}
}
private static final class AlphabeticalOrder implements Comparator<ServiceTypes> {
#Override
public int compare(final ServiceTypes o1, final ServiceTypes o2) {
return o1.toString().compareTo(o2.toString());
}
}
}
When I test getOrderedServiceTypes(), I see Zero("zero", 6, true) as the first element , which is incorrect.
Seems like values() is not sorted based on the comparator.
How can I run the custom orders on Enum?
No, what's happening is that values() creates a new array each time, so you're sorting an array of the values and then returning a completely different array.
All you need to do is e.g.
ServiceTypes[] values = values();
Arrays.sort(values, orderComparator);
return values;
Seems like values() is not sorted based on the comparator.
Indeed. As per the specification (section 8.9.2):
(values[]) Returns an array containing the constants of this enum
type, in the order they're declared.
Sorting the array returned by values once isn't going to have any effect - a new array will be handed out on the next call. You can do this though:
public static ServiceTypes[] getNamedServiceTypes() {
ServiceTypes[] array = values();
Arrays.sort(array, nameComparator);
return array;
}
Or I would personally create an immutable list of them sorted each way you care about, and return that same list reference each time.
Just another observation. You are initializing both orderComparator and nameComparator as objects of OrderComparator. You need to take a look at it if it's not intended.

Categories