How i can create Generic Method in Java? - java

I have two a conditions in the method:
if(urlSendModel.isHasPhoto()) {
ArrayList<InputMediaPhoto> inputMediaPhotos = new ArrayList<>();
for(String photoUrl : urlSendModel.getPhotos()){
inputMediaPhotos.add(new InputMediaPhoto(photoUrl));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMediaPhotos.toArray(new InputMediaPhoto[0]));
bot.execute(sendMediaGroup);
}
if(urlSendModel.isHasVideo()){
ArrayList<InputMediaVideo> inputMediaVideos = new ArrayList<>();
for(String videoUrl : urlSendModel.getVideos()){
inputMediaVideos.add(new InputMediaVideo(videoUrl));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMediaVideos.toArray(new InputMediaVideo[0]));
bot.execute(sendMediaGroup);
}
How can I create something like this or solve the problem in another way.
private <T extends InputMedia<T>> void sendMedia(Message message, ArrayList<String> urls) {
ArrayList<T> inputMedia = new ArrayList<>();
for(String url : urls){
inputMedia.add(new T(url));
}
SendMediaGroup sendMediaGroup = new SendMediaGroup(message.chat().id(),
inputMedia.toArray(new T[0]));
bot.execute(sendMediaGroup);
}
I will be glad to any proposed solution.

Both of the requirements here can be gotten around by passing the class into the method. I'll skip the additional details of what your method does, but, for instance:
<T> void doSomething(final Class<T> klass, final int length) {
// Replace 'new T[10]'
final T[] array = Array.newInstance(klass, length);
final Constructor<T> constructor = klass.getConstructor();
for (int i = 0; i < length; i++) {
// Replace 'new T()'
array[i] = constructor.newInstance();
}
}
Note 1: The replacement for new T() was provided by #Vince Emigh.
Note 2: Exception handling is not considered here, so this will not compile as-is.
Note 3: If you need to use a different constructor to the one with no arguments, then it may well be simpler to accept a Function<Object[], T> which will convert the arguments you provide to an instance of the type.

You can do something like creating the generic class first :
public class ClassList<T extends Object>{ private ArrayList<T> list; .... }
// or
public class ClassList<T extends InputMedia>{ private ArrayList<T> list; .... }
and then you cas use constructor or setter to affect a value to your attribute list of T

Related

Java return nested type parameters

I have a problem with Java's Generic System.
In my program is a wrapper for lists, that should have a method to return it's inner list:
public class Wrapper<T, S extends List<T>> {
private S list;
public Wrapper(S list) {
this.list = list;
}
public S get() {
return list;
}
}
Then there is a Context that holds a Map with different Wrappers and a method that returns the list of the wrapper associated with the id:
public class Context {
private Map<String, Wrapper> map;
public Wrappers() {
map.put("a", new Wrapper(ArrayList<String>());
map.put("b", new Wrapper(LinkedList<Integer>());
}
public <T, S extends List<T>> S getList(String id) {
return map.get(id).get();
}
}
Now when I call getList() I want to have a compiler warning or at least a way to realise an error before a ClassCastException gets thrown.
public class Receiver {
public doSomething() {
Context c = new Context();
c.createWrappers();
// Ok
ArrayList<String> list1 = c.getList("a");
LinkedList<Integer> list2 = c.getList("b");
// Compiler error or some way do check in getList().
ArrayList<Integer> list3 = c.getList("a");
LinkedList<String> list4 = c.getList("b");
}
}
I've actually tried a lot of things like changing the Wrapper definition to:
public class Wrapper<T, S extends List>
But when I want to implement the get() function I run into a problem I can either define the function like this:
public List<T> get() {
return list;
}
or like this
public S get() {
return list;
}
In the first example it would still be possible to do this.
public doSomething() {
//...
LinkedList<String> list = c.getList("a");
}
and in the second example it would be possible to do this.
public doSomething() {
//...
ArrayList<Integer> list = c.getList("a");
}
Is there any way to define the get method in a way like this?
public S<T> get() {
return list;
}
It seems to me like there is no way to check both the type of the list and the type of the elements at the same time.
The compiler has no way of knowing what return type is associated with the particular string you passed (strings cannot be made type-safe).
However, you could replace strings with type-safe marker objects:
class ListId<T> {
public ListId(string name) { ... }
public static final ListId<ArrayList<String>> A = new ListId<>("a");
public static final ListId<LinkedList<Integer>> B = new ListId<>("b");
}
public T getList<T>(ListId<T> id)

Class type reference not pass in java generic class or method

This is the method which call the generic class method.
ServiceHandler<MyClass> serviceHandler = new ServiceHandler<MyClass>();
List<MyClass> res = serviceHandler.genericGetAll("https://api.github.com/users/hadley/orgs", MethodTypes.GET.toString(), null);
Here is the generic class with generic method
public class ServiceHandler<T>{
public ServiceHandler() {
}
public List<T> genericGetAll(String destinationUrl, String method, HashMap<String, String> params) {
List<T> genericResponse = null;
String httpResponse = httpClient(destinationUrl, method, params);
genericResponse = createListResponseHandler(httpResponse);
return genericResponse;
}
private List<T> createListResponseHandler(String string_response) {
return gson.fromJson(string_response, new TypeToken<List<T>>() {
}.getType());
}
Now the problem is I never get the MyClass reference in T. Where I am doing wrong? . I have done lots of google but still not found the solution.
My question is how to get MyClass reference in T. Because I need to serialized the data using gson.
I have tried. But still no solution
List<MyClass> res = serviceHandler.<MyClass>genericGetAll("https://api.github.com/users/hadley/orgs", MethodTypes.GET.toString(), null);
Generic type tokens don't work.
You need to pass in the new TypeToken<List<MyClass>>() {} as a constructor parameter to ServiceHandler:
public class ServiceHandler<T> {
private final TypeToken<List<T>> typeToken;
public ServiceHandler(TypeToken<List<T>> typeToken) {
this.typeToken = typeToken;
}
// ...
private List<T> createListResponseHandler(String string_response) {
return gson.fromJson(string_response, typeToken.getType());
}
}
and instantiate using:
ServiceHandler<MyClass> serviceHandler =
new ServiceHandler<>(new TypeToken<MyClass>() {});

How to react on empty Lists using wildcards as type?

I wanna set a List in a class using a wildcard as type:
public List<?> setList(List<?> list){
this.list = list;
}
But I only will accept some types of the List elements. The example contains only the String approach but I will accept String, Double or Integer f.e..
I know this approach:
public void setList(List<?> list){
if(!list.isEmpty())
if(list.get(0).getClass().equals(String.class))
this.list = list;
else
throw new IllegalArgumentException();
else
// what to do?
}
If the List isn't empty I can check the first elements class. If it's of the String.class I will set the List, otherwise an Exception will be thrown.
But how should I react if the List is empty? Because AFAIK I can't check the type of the wildcard because of type erasure. So, I can't instanciate a empty List.
Should I just do this.list = null;? Or should I only accept not empty Lists and throw an Exception if it's empty? I appreciate any suggestion. Because this code will be reused and the guy who works with it should be informed about what happens.
To answer directly to your question: there will be no problem with an empty list.
Because of type erasure there is no difference between List<String>, List<Double> or simple List at runtime.
You can even fool the compiler and do things like
List<String> stringList = new ArrayList<String>();
stringList.add("foo");
List rawList = stringList;
rawList.add(42);
List<Integer> integerList = rawList;
having three references to the same list with different treatment only by the compiler. (You will get some warnings dependend on your compiler settings, but that's all.)
If you want to check the consistency at runtime, you have to check every single member of the list
public <T> void setList(List<T> list, Class<T> clazz) {
for (T t : list) {
if(!clazz.equals(t.getClass())) {
throw new YourPreferedKindOfException();
}
}
this.list = list;
}
(check for null arguments omitted)
Suitable exception could be IllegalArgumentException, called e.g.
setList(stringList, String.class);
To be realy sure at runtime you have to copy the list, because your function got the list as reference and the content of it can be modified outside your function later.
I have three approaches here:
SIMPLE GENERIC APPROACH
You have to parametrize the class holding the list. By creating a new instance with a specified generic type. That would be a type-safe approach.
public class ListHolder<T> {
private List<T> list;
public void setList(List<T> list) {
this.list = list;
}
public static void main(String[] args) {
ListHolder<String> sl = new ListHolder<String>();
sl.setList(new ArrayList<String>());
ListHolder<Double> dl = new ListHolder<Double>();
dl.setList(new ArrayList<Double>());
}
}
DYNAMIC PERMISSION BASED APPROACH
Here we populate a set with classes that are allowed.
public class ListHolder {
private Set<Class<?>> allowed = new HashSet<Class<?>>();
private List<?> list;
public <T> void setList(List<T> list) {
if (list.isEmpty()) {
// problem is we can't check type here. Exception would be better
list = Collections.<T> emptyList();
} else {
Class<?> clazz = list.get(0).getClass();
if (allowed.contains(clazz)) {
this.list = list;
} else {
throw new IllegalArgumentException("Type "
+ clazz.getSimpleName() + " not allowed!");
}
}
}
public void allowClass(Class<?> clazz) {
allowed.add(clazz);
}
}
STATIC PERSMISSION BASED APPROACH
Here the checking takes place at compile time.
public class ListHolder {
private List<?> list;
public static final Allowed<String> STRING = new Allowed<String>();
public static final Allowed<Double> DOUBLE = new Allowed<Double>();
public <T> void setList(List<T> list, Allowed<T> permission) {
if (permission == null)
throw new NullPointerException("Permission is null!");
this.list = list;
if (list == null) // if you don't want list to be null
this.list = Collections.<T>emptyList(); // added type parameter to make it clearer for reader
// ...compiler does that implicitly
}
private static class Allowed<T> {
}
public static void main(String[] args) {
ListHolder l = new ListHolder();
List<String> strings = new ArrayList<String>();
List<Double> doubles = new ArrayList<Double>();
List<Integer> integers = new ArrayList<Integer>();
l.setList(strings, ListHolder.STRING);
l.setList(doubles, ListHolder.DOUBLE);
//l.setList(integers, ListHolder.DOUBLE); Does not even compile
}
}

Generic type creator [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Java Generics: Cannot cast List<SubClass> to List<SuperClass>?
I want to create function on some base class, that create it inheritance classes.
I have try this:
class BaseFormat
{
// Some variables
public BaseFormat(string txt)
{
// Do something
}
public static <T> ArrayList<T extends BaseFormat> getTextFormat(String[] txt)
{
ArrayList<T> list = new ArrayList<T>();
for (int i = 0; i < txt.length; i++)
{
list.add(new T(txt[i])); // ERROR
}
return list;
}
}
class FooFormat extends BaseFormat
{
// Some variables
public FooFormat (string txt)
{
// Do something
}
}
And this:
class BaseFormat
{
// Some variables
public BaseFormat(string txt)
{
// Do something
}
public static ArrayList<BaseFormat> getTextFormat(String[] txt)
{
ArrayList<T> list = new ArrayList<T>();
for (int i = 0; i < txt.length; i++)
{
list.add(new BaseFormat(txt[i]));
}
return list;
}
}
But when I try to cast the array I receive an error. This is my code:
String[] txts = ...; // Some texts
ArrayList<FooFormat> list = (ArrayList<FooFormat>) BaseFormat.getTextFormat(txts); // Casting ERROR
So how can I do it, but still keep it generic?
You would have to pass the type in as a parameter to the static method, and then probably use reflection to invoke Class.newInstance. Type erasure means you will not have a concrete type T after compilation, so that is why you can't compile new T(...)
try doing
ArrayList<BaseFormat> list = (ArrayList<BaseFormat>) BaseFormat.getTextFormat(txts);
Then on iterating you can down cast the items to FooFormat with check of instanceOf operator
So, you are mixing generics, which allow dynamic typing, with inheritance, which allows overriding methods. What you really want is to separate creating the wrapped string from creating the list.
class BaseFormat
{
// derived classes override this method to provide their own implementations
public abstract BaseFormat wrapText(String[] txt);
public ArrayList<? extends BaseFormat> getTextFormat(String[] txt)
{
ArrayList<? extends BaseFormat> list = new ArrayList<BaseFormat>();
for (int i = 0; i < txt.length; i++)
{
list.add(wrapText(txt);
}
return list;
}
}
Are you trying to do something as follows
class BaseFormat { }
class FooFormat extends BaseFormat { }
class FormatUtils {
public static <T extends BaseFormat> List<T> getTextFormat(String[] txt, Class<T> clazz) {
List<T> list = new ArrayList<T>();
//...
T t = clazz.newInstance(); //create instance using reflection
//...
return list;
}
}
And do
List<FooFormat> list = FormatUtils.getTextFormat(new String[]{}, FooFormat.class);

How can I cast Generics in Java?

I have this code:
FVDTO.setStatus("fail");
List<String[]> invalidFields = new ArrayList<String[]>();
Iterator<ConstraintViolation<HazardSubmission>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<HazardSubmission> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
FVDTO.setInvalidFields(invalidFields);
return new JsonResolution(FVDTO);
I've taken some out to keep things DRY so I can then use it with other classes, i.e HazardSubmission is one class, and there will be others. The below code shows my attempt, obviously manually casting <HazardSubmission> here won't work it needs to be like o.getClass();
public static List<String[]> GetInvalidProperties(Set<ConstraintViolation<Object>> cv, Object o) {
List<String[]> invalidFields = new ArrayList<String[]>();
Iterator<ConstraintViolation<HazardSubmission>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<HazardSubmission> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
}
The second code block fails because I don't really know what I'm doing, I want to pass in the cv for param 1, with a general object type, then somehow pass in the type as a second paramter.
Could you someone please explain how to do this?
I think you might be looking for a generic method
public static <T> List<String[]> GetInvalidProperties(Set<ConstraintViolation<T>> cv){
Iterator<ConstraintViolation<T>> iterator = cv.iterator();
while(iterator.hasNext()) {
ConstraintViolation<T> i = iterator.next();
String property = i.getPropertyPath().toString();
String message = i.getMessage();
invalidFields.add(new String[] { property, message });
}
}
If all T extends a given class or interface you could even say
public static <T extends MyClassOrInterface> List<String[]> GetInvalidProperties(Set<ConstraintViolation<T>> cv){
//...
}
cv.iterator() will return you an Iterator<ConstraintViolation<Object>>, and you need Iterator<ConstraintViolation<HazardSubmission>>. This is done because cv is defined as Set<ConstraintViolation<Object>>. If you want a generic way for this, you can change
Set<ConstraintViolation<Object>> cv
to
Set<ConstraintViolation<? extends Object>> cv
in that way your code will compile.

Categories

Resources