I am new to java generics and below is the code that has created a confusion for me to pass generics class as an argument to the method.
I have created an android project where i have used Volley library to handle server calls.Below is the code
Advanced Connection Util : this class returns the JacksonRequest object
public class AdvancedConnectionUtil<T> {
private String requestType;
private ServerListener listener;
public AdvancedConnectionUtil(String requestType , ServerListener<T> listener){
this.listener = listener;
this.requestType = requestType;
}
public JacksonRequest getRequest(){
//This gives compile error while while passing DataList.class in the argument
return new JacksonRequest<T>(Request.Method.GET, HttpRequestConstant.JACKSON_FETCH, null ,DataList.class, new Response.Listener<T>() {
#Override
public void onResponse(T response) {
listener.onDataReceived(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onErrorReceived(error.getMessage());
}
});
}
public interface ServerListener<T> {
public void onDataReceived(T data);
public void onErrorReceived(String errorMsg);
}
}
Custom JacksonRequest class : this class handles the server call and the success call backs
public class JacksonRequest<T> extends JsonRequest<T> {
private Class<T> responseType;
/**
* Creates a new request.
* #param method the HTTP method to use
* #param url URL to fetch the JSON from
* #param requestData A {#link Object} to post and convert into json as the request. Null is allowed and indicates no parameters will be posted along with request.
* #param responseType
* #param listener Listener to receive the JSON response
* #param errorListener Error listener, or null to ignore errors.
*/
public JacksonRequest(int method, String url, Object requestData, Class<T> responseType, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(method, url, (requestData == null) ? null : Mapper.string(requestData), listener, errorListener);
this.responseType = responseType;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return super.getHeaders();
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(Mapper.objectOrThrow(json, responseType), HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
}
Here is My activity class that creates a request and pass it to other method to make a server call
public class CustomJacksonRequestActivity extends SuperActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showProgressBar();
JacksonRequest jacksonRequest = new AdvancedConnectionUtil<DataList>(null, httpListener).getRequest();
//This commented code works fine when i create a request this way
/* JacksonRequest<DataList> jacksonRequest = new JacksonRequest<DataList>(Request.Method.GET, HttpRequestConstant.JACKSON_FETCH, null, DataList.class, new Response.Listener<DataList>() {
#Override
public void onResponse(DataList response) {
hideProgressBar();
Log.e("ANSH", "onResponse : " + response.getPicture());
// fillListWithIndex(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
hideProgressBar();
Log.e("ANSH", "onErrorResponse : " + error.getLocalizedMessage());
}
});*/
onExecute(jacksonRequest);
}
#Override
protected void internetAvailable() {
}
#Override
public void setTitle(CharSequence title) {
super.setTitle("CustomJacksonActivity");
}
private AdvancedConnectionUtil.ServerListener httpListener = new AdvancedConnectionUtil.ServerListener<DataList>() {
#Override
public void onDataReceived(DataList data) {
Log.e("ANSH", "onResponse : " + data.getPicture());
}
#Override
public void onErrorReceived(String errorMsg) {
Log.e("ANSH", "onResponse : " + errorMsg);
}
};
Now the problem is i am not able to pass DataList.class(This is the response model class) as a parameter in the constructor of JacksonRequest Class inside the getRequest method of AdvancedConnectionUtil class though i am able to do that when i was creating the request object in the activity(see the commented code in the activity).
How can i pass the DataList.class to the constructor of JacsonRequest?
The updated code is in git hub now
github link to the project
Your code is a little confusing (you could do with a simpler example), but I'll give it a shot...
The JacksonRequest class takes one generic type parameter <T> and the type of 4th constructor parameter references this type: , Class<T> responseType,. That means that when an instance of JacksonRequest is instantiated (with T being a real type), the 4th parameter passed must guarantee to be of type T.class.
When you call the constructor...
return new JacksonRequest<T>(Request.Method.GET, blah, null ,DataList.class, ...
... you are calling it with a generic type argument <T>. The compiler must always be able to match generic type arguments with generic type parameters, but with the 4th parameter you are asking it to match T with DataClass. Since the compiler does not know what T is (or more precisely, it cannot guarantee that when it is instantiated, T will actually be DataClass), it gives an error.
Generally speaking, you cannot mix generic type parameters and real type values - you must consistently choose one or the other. There are options which allow you to specify that a generic type parameter will derive from a class or implement an interface (<T super DataClass> or <T extends DataClass>, but it's a bit much to explain here.
The problem with your code is that :
Your JacksonRequest<T> is declared to take a listener that must be parameterized on the same type as your responseType
However with your AdvancedConnectionUtil<T>.getRequest(...), there is no guarantee that the JacksonRequest created will meet the above requirement. Because you can always write code like:
new AdvancedConnectionUtil<String> (null, httpListener).getRequest();
So the parameter you passed into the constructor of JacksonRequest will be a DataList.class (of type Class<DataList>) and a listener of type Listener<String>
Sadly there is no way to do T.class in Java, although that indeed feels like what you need. Usually in such cases we will declare the AdvancedConnectionUtil as:
class AdvancedConnectionUtil<T> {
private final Class<T> responseType;
private final ServerListener<T> serverListener;
private final String requestType;
public AdvancedConnectionUtil (String requestType , Class<T> responseType, ServerListener<T> listener) {
this.requestType = requestType;
this.responseType = responseType;
this.serverListener = listener;
}
public JacksonRequest<T> getRequest(){
return new JacksonRequest<T>(0, "", null ,responseType, new Response.Listener<T>(){
...
}
}
You need pass your responseType of Class<T> into AdvancedConnectionUtil and keep it as a member field. This way the AdvancedConnectionUtil instance, when created, is strictly limited to provide JacksonRequest bounded to a specific response type.
You could in theory declare your getRequest method as getRequest(Class<T> responseType). But your AdvancedConnectionUtil does not seem to gain anything from that
Related
So, I'm trying to connect to Scryfall's API and do an autocomplete call. I've been able to use their other call properly but this one I think where I'm having issue.
Here is the call: https://api.scryfall.com/cards/autocomplete?q=fire
q is the query and it will return a list of up to 20 items that could be auto-completed with the word 'fire'.
{
"object":"catalog",
"total_values":20,
"data": [
"Fire // Ice","Fire Imp","Firefly","Fire Whip","Fire Ants","Firebolt","Fireball","Fire Drake","Fire Snake","Firespout","Firestorm","Fireblast","Fire-Field Ogre","Fire Urchin","Fire Bowman","Fire Dragon","Fire at Will","Fire Ambush","Firemaw Kavu","Fire Juggler"
]
}
I am using retrofit2 for android.
Here is some of my code.
This is my interface for the endpoints
public interface ScryfallEndPoints {
//https://api.scryfall.com/cards/named?fuzzy=
#GET("cards/named")
Call<Card> getCard(
#Query(value=("fuzzy")) String name);
//https://api.scryfall.com/cards/autocomplete?q=
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
This is a method I use in my activity to perform the call.
private void loadCardList()
{
final ScryfallEndPoints apiService =
APIClient.getClient().create(ScryfallEndPoints.class);
Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Map<String, String>>()
{
#Override
public void onResponse(Call<Map<String, String>> call, Response<Map<String, String>> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Map<String, String>> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
//tv.setText(str);
}
Here is a method that is part of my model class.
#SerializedName("data")
private Map<String, String> cardList;
public Map<String, String> getCardList() {return cardList;}
So, I feel like there is definitely something maybe wrong in the way I am trying to access he data in my model class and maybe with the way I have it set up in my interface. When I make the call, it doesn't fail so I don't have error logs to show, i just know that it is going to the onFailure() method and I'm not sure why. I mostly need to figure this out then I can work on getting the list to populate. Also, if there is a way that I can see more of what is going on with the calls that I am making, that would be great too. Thanks!
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
Then the calling part
//Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
//It returns Call of Card type, hence it will be as follows
Call<Card> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Card>()
{
#Override
public void onResponse(Call<Card> call, Response<Card> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Card> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
i have this code :
public class WeatherLoader extends AsyncTaskLoader {
/** Tag for log messages */
private static final String LOG_TAG = WeatherLoader.class.getName();
private String mUrl;
private int mDataWeatherType;
public WeatherLoader(Context context, String url , int dataWeatherType) {
super(context);
mUrl = url;
mDataWeatherType = dataWeatherType;
}
#Override
public Object loadInBackground() {
Log.i(LOG_TAG , "TEST : loadInBackground() called ...");
if(mUrl == null){
return null;
}
if( mDataWeatherType == 1) {
CurrentWeather currentWeather = QueryUtils.fetchCurrentWeatherData(mUrl);
return currentWeather;
}else if(mDataWeatherType == 2) {
List<HourForecast> hourlyForecastsList = QueryUtils.fetchHourlyForecastsData(mUrl);
return hourlyForecastsList;
}else {
List<DayForecast> dailyForecastsList= QueryUtils.fetchDailyForecastsData(mUrl);
return dailyForecastsList;
}
}
}
in the main activity :
#Override
public Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
}
#Override
public void onLoadFinished(Loader<List<HourForecast>> loader, List<HourForecast> data) {
mHourForecastAdapter.clear();
mHourForecastAdapter.addAll(data);
}
#Override
public void onLoaderReset(Loader<List<HourForecast>> loader) {
mHourForecastAdapter.clear();
}
in the AsyncTaskLoader i do not specify generic type, and in the LoaderManager.LoaderCallbacks<List<HourForecast>> i specify generic type,
the code work correctly.
Could someone explain me how the result of loadInBackground gets passed on to onLoadFinished? I'm asking this as loadInBackground returns an Object and onLoadFinished accepts a List<HourForecast> and not an Object.
In java using generics removes the need for cast by the programmer and object in java can be anything, since its OOP every class extends Object by default.
In you case AsyncTaskLoader has a generic that extends Loader. If you do not specify the object with generic, the return object is Object.
Which means in the method
Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
You already are creating WeatherLoader you cast it to Loader (which is superclass of AsyncTaskLoader) And you cast it to Loader<List<HourForecast>> there for you get you list when you call
#Override
public Object loadInBackground()
However, this is a very bad example of generics you have there. Generics are made to eliminate runtime errors, and your example just makes more places to have a runtime error. And Please don't use AsyncTasks :) They are the evil. Read some basic books on android programming, it teaches you to use handlers. The ultimate solution to your threading would be RxJava, but its more for advanced programmers.
I would like to send and email on the background of my app.
I followed this example and implemented it
http://javapapers.com/android/android-email-app-with-gmail-smtp-using-javamail/
public class SendMailTask extends AsyncTask {
private ProgressDialog statusDialog;
private Activity sendMailActivity;
public SendMailTask(Activity activity) {
sendMailActivity = activity;
}
protected void onPreExecute() {
statusDialog = new ProgressDialog(sendMailActivity);
statusDialog.setMessage("Getting ready...");
statusDialog.setIndeterminate(false);
statusDialog.setCancelable(false);
statusDialog.show();
}
#Override
protected Object doInBackground(Object... args) {
try {
Log.i("SendMailTask", "About to instantiate GMail...");
publishProgress("Processing input....");
GMail androidEmail = new GMail(args[0].toString(),
args[1].toString(), (List) args[2], args[3].toString(),
args[4].toString());
publishProgress("Preparing mail message....");
androidEmail.createEmailMessage();
publishProgress("Sending email....");
androidEmail.sendEmail();
publishProgress("Email Sent.");
Log.i("SendMailTask", "Mail Sent.");
} catch (Exception e) {
publishProgress(e.getMessage());
Log.e("SendMailTask", e.getMessage(), e);
}
return null;
}
#Override
public void onProgressUpdate(Object... values) {
statusDialog.setMessage(values[0].toString());
}
#Override
public void onPostExecute(Object result) {
statusDialog.dismiss();
}
}
The code is working fine
However, i have a presentation and i need to explain the code.
In the code, SendMailTask extends AsyncTask without any extra parameters not even Void
I m stuck in this point because i searched and no one is using this way.
Can anyone explain it to me?
without the parameters, AsyncTask will assume the default class (which is Object)
public class SendMailTask extends AsyncTask <Object1, Object2, Object3> {
#Override
protected Object doInBackground(Object1... args) {
...
//publishProgress calls "onPublishProgress" method
//e.g. publishProgress("email sent.");
publishProgress(object2);
//last line returns to "onPostExecute" method
//e.g. return null;
return object3;
}
#Override
public void onProgressUpdate(Object2... values) {
...
}
#Override
public void onPostExecute(Object3 result) {
...
}
}
Object1 is the array of parameters you pass in when initializing the asyncTask
new SendMailTask(SendMailActivity.this).execute(fromEmail,
fromPassword, toEmailList, emailSubject, emailBody);
so fromEmail, fromPassword, etc, etc all goes into the array of Object1.
you access them in doInBackground method using arg[ index ].
If you not specify any Generic parameter , You will see it will take Object as a type (Base Class for all Classes)
So in case of using only AsyncTask
You are actually dealing with Object
eg- See the parameter of doInBackground() in the given link
Honestly, the way they've extended AsyncTask isn't terribly smart. It causes a number of annoying warnings in Android Studio, and it would have been just as easy to extend AsyncTask<Object, Object, Object>, it's the same effect, but it removes the warnings.
All of that said, in order to use the class, you can simply call the following:
Object[] args = new Object[]{
arg1,
arg2,
etc
};
new SendMailTask(activity).execute(args);
You just need to check the GMail constructor to see how you should order your args.
In my program, the user needs to input what type of players the game will have. The players are "human", "good" (for a good AI), "bad" (for a bad AI) and "random" (for a random AI). Each of these players have their own class that extend one abstract class called PlayerType.
My struggle is mapping a String to the object so I can A) create a new object using the String as sort of a key and B) get the related String from an object of its subclass
Ultimately, I just want the implicit String to only appear once in the code so I can change it later if needed without refactoring.
I've tried using just a plain HashMap, but that seems clunky with searching the keys via the values. Also, I'm guessing that I'll have to use the getInstance() method of Class, which is a little less clunky, which is okay if it's the only way.
What I would do is create an enum which essentially functions as a factory for the given type.
public enum PlayerTypes {
GOOD {
#Override
protected PlayerType newPlayer() {
return new GoodPlayer();
}
},
BAD {
#Override
protected PlayerType newPlayer() {
return new BadPlayer();
}
},
RANDOM {
#Override
protected PlayerType newPlayer() {
return new RandomPlayer();
}
};
protected abstract PlayerType newPlayer();
public static PlayerType create(String input) {
for(PlayerTypes player : PlayerTypes.values()) {
if(player.name().equalsIgnoreCase(input)) {
return player.newPlayer();
}
}
throw new IllegalArgumentException("Invalid player type [" + input + "]");
}
)
Because then you can just call it like so:
String input = getInput();
PlayerTypes.create(input);
Of course, you'll get an IllegalArgumentException which you should probably handle by trying to get the input again.
EDIT: Apparently in this particular case, you can replace that loop with just merely
return PlayerTypes.valueOf(input).newPlayer();
And it'll do the same thing. I tend to match for additional constructor parameters in the enum, so I didn't think of using valueOf(), but it's definitely cleaner.
EDIT2: Only way to get that information back is to define an abstract method in your PlayerType class that returns the PlayerTypes enum for that given type.
public class PlayerType {
public abstract PlayerTypes getType();
}
public class GoodPlayer extends PlayerType {
#Override
public PlayerTypes getType() {
return PlayerTypes.GOOD;
}
}
I like the answer provided by Epic but I don't find maps to be clunky. So it's possible to keep a map and get the constructor call directly.
Map<String, Supplier<PlayerType> map = new HashMap<>();
map.put("human", Human::new);
Human h = map.get("human").get();
The two main options I can think of:
Using Class.newInstance(), as you mentioned (not sure if you had this exact way in mind):
// Set up your map
Map<String, Class> classes = new HashMap<String, Class>();
classes.put("int", Integer.class);
classes.put("string", String.class);
// Get your data
Object s = classes.get("string").newInstance();
You could use Class.getDeclaredConstructor.newInstance if you want to use a constructor with arguments (example).
Another option is using switch:
Object getObject(String identifier) {
switch (identifier) {
case "string": return new String();
case "int": return new Integer(4);
}
return null; // or throw an exception or return a default object
}
One potential solution:
public class ForFunFactory {
private ForFunFactory() {
}
public static AThing getTheAppropriateThing(final String thingIdentifier) {
switch (thingIdentifier) {
case ThingImplApple.id:
return new ThingImplApple();
case ThingImplBanana.id:
return new ThingImplBanana();
default:
throw new RuntimeException("AThing with identifier "
+ thingIdentifier + " not found.");
}
}
}
public interface AThing {
void doStuff();
}
class ThingImplApple implements AThing {
static final String id = "Apple";
#Override
public void doStuff() {
System.out.println("I'm an Apple.");
}
}
class ThingImplBanana implements AThing {
static final String id = "Banana";
#Override
public void doStuff() {
System.out.println("I'm a Banana.");
}
}
First off - I'm rather novice at Java so if the question makes no sense do let me know.
Basically I'm making an Android app which communicates with my web service and so I've made a separate class to deal with the communication, which also includes the AsyncTask (I've removed a lot from the code here just for preview):
public class api {
private String caller = null;
Context that = null;
api(Context that) {
this.that = that;
this.caller = that.getClass().getSimpleName();
}
void call(String action) {
/* .... */
}
new back().execute(param1, param2);
}
void callback(String action, String result){
that.callback(action, result);
}
public class back extends AsyncTask<String, Void, String> {
public String response = null;
protected String doInBackground(String... params) {
response = connection.executeRequest(params[1]);
return response;
}
protected void onPostExecute(String result) {
callback("a", "b");
}
}
}
And when I use the class from some part of the app (let's say SomeClass.class), I do:
api WS = new api(this);
WS.call("....");
And it's supposed to execute the function 'callback' which is in SomeClass.
But the key problem here is this line:
that.callback(action, result);
Eclipse makes me add the name of the "caller" class in the cast:
(SomeClass) that.callback(action, result);
But that doesn't work for me, because I use the 'api' class from many different classes, so ideally I need to put a variable in the cast. I do get the name of the "caller" class here:
this.caller = that.getClass().getSimpleName();
//obviously this won't work:
(this.caller) that.callback(action, result);
Is there anyway to do that, or am I doing something fundamentally wrong?
Thank you.
Currently your api class accepts a Context object in its default constructor. It would make more sense to extend Context with a new class which contains a callback method which you can then override in subclasses such as SomeClass, that would negate the need for casting in your api class. e.g:
public class APIContext extends Context
{
public void callback( String action, String result )
{
/* ... */
}
}
public class SomeClass extends APIContext
{
#Override
public void callback( String action, String result )
{
/* ... */
}
}
public class api
{
private APIContext callerContext = null;
public api( APIContext context )
{
this.callerContext = context;
}
public void callback( String action, String result )
{
callerContext.callback( action, result );
}
}