How can I give a promise to the compiler that what I am doing is type safe?
I have a class "Container" that can have some info. I extend said class into what I will refer to as EContainer
(E for Extended) and define that it can have some specific info, like say an int
and a string
. Now, I want to be able to make a list of EContainers
and sort it by either int
or string
, and my solution was to have a ContainerList<T>
class where T
is what you sort by, and you pass in a lambda to act as an adapter for EContainer
to extract T
.
So far so good, however now say I want to delimit the list in some way into groups, so if the hypothetical EContainer
has ints ranging from 0 to 100, I would want to say "group EContainer
s like this: 0-20, 21-50, 51-100".
My initial guess was that I would add a function I'll refer to as getDelimiter
inside EContainer
that would be able to return the "group" it is in, utilizing a protected predefined enum that would define delimiters, however, this is where I ran into problems - the way I would return a group is by having a shared value of T
that multiple EContainers
would return after running getDelimiter
, and so I would group based on that, but unfortunately that would mean that getDelimiter
would be a multi-type return function and I do not know how exactly I would tell the compiler that "yes, I know what I am doing, and that I will definitely get back T
from getDelimiter
"
I could probably get around this by adding another lambda in there that would define the function to call on the EContainer
to get the delimiter function, and that should be type safe and everything, but I thought that would end up in an unnecessary boilerplate in EContainer
, so maybe there is a better option I don't know of? I have to say, it is very difficult to find the keywords I could use for this problem, so I couldn't do much initial research.
Edit: Just had an idea of having getDelimiter
return everything as Object
and then casting to T
right after, sounds like that should work and would minimize boilerplate.
CodePudding user response:
How can I give a promise to the compiler that what I am doing is type safe?
Generally speaking, that is what the @SuppressWarnings("unchecked")
does.
It is not clear if it will work for your use-case. (Show us your actual code ... if you want a more specific answer.)
However, there will still be a hidden runtime check to detect that you are not violating runtime type safety. There is no way you can turn off those checks.
Just had an idea of having
getDelimiter
return everything asObject
and then casting toT
right after, sounds like that should work and would minimize boilerplate.
Hmm ... you can't cast something statically typed as Object
to T
. The fundamental problem is that the actual type of T
is erased, so the runtime doesn't know what type to cast to.
One possible workaround is to represent the actual type of T
as a Class
object, and use reflection to do the type check / cast. Indeed, this is the general workaround when you need to do type checks in spite of type erasure. Unfortunately this is cumbersome, because at some point you need to pass an extra explicit Class
object as a parameter to make it work.
CodePudding user response:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
public class StackOverflowNonsense {
public static abstract class Container{
public String sharedFunc(){return "this is a shared function";}
}
public static class EContainer{
public int _int; //id=0
public float _float; //id=!0
public EContainer(int _int,float _float){
this._int=_int;
this._float=_float;
}
protected enum Delimiters{
INT(new ArrayList<Object>(Arrays.asList(0,21,51))),
FLOAT(new ArrayList<Object>(Arrays.asList((float)0,(float)0.5,(float)1)));
final ArrayList<Object> delim;
Delimiters(ArrayList<Object> delim){
this.delim=delim;
}
}
public Object getDelimiter(int id){
if (id==0){
int pos=Collections.binarySearch(Delimiters.INT.delim, (Object)_int,Comparator.comparingInt(key->(int)key));
return (Object)Delimiters.INT.delim.get(pos<0?-2-pos:pos);
}else{
int pos=Collections.binarySearch(Delimiters.FLOAT.delim, (Object)_float,Comparator.comparingDouble(key->(Float)key));
return (Object)Delimiters.FLOAT.delim.get(pos<0?-2-pos:pos);
}
}
@Override
public String toString(){
return String.format("EContainer with (%d, %f)", _int,_float);
}
}
public static void main(String[] args){
ArrayList<EContainer> ContainerList=new ArrayList<>(
Arrays.asList(new EContainer(0,(float)2.0),new EContainer(69,(float)0.69))
);
Collections.sort(ContainerList,Comparator.comparingInt(key->(int)key.getDelimiter(0)));
System.out.println(ContainerList);
Collections.sort(ContainerList,Comparator.comparingDouble(key->(Float)key.getDelimiter(1)));
System.out.println(ContainerList);
}
}
This appears to provide the functionality I want, however I'm not sure if this code breaks some holy scripts, so maybe there is a better solution?