2

This is a continuation of my earlier problem. My question there is solved but I think to further ask about the code, it's better for me to open another question, since I also need to ask some other thing about it. So here's my observable

public static <T> Observable<T> makeObservable(final Callable<T> func) {
        return Observable.create(
                new Observable.OnSubscribe<T>() {
                    @Override
                    public void call(Subscriber<? super T> subscriber) {
                        try {
                            T observed = func.call();
                            if (observed != null) { // to make defaultIfEmpty work
                                subscriber.onNext(observed);
                            }
                            subscriber.onCompleted();
                        } catch (Exception ex) {
                            subscriber.onError(ex);
                        }
                    }
                }).map(new Func1<T, T>() {
            @Override
            public T call(T t) {
                return null;
            }
        });

I don't know what is that T doing there? I tried to googled but I can't understand its role in the code above. Official java doc says its a 'A generic type is a generic class or interface that is parameterized over types'. Tried to search further about it and I came upon this question that confuses me even more.

Apart from the need to really understand the T there, I need to know how to use .map to transform the content of T. From the code above, this segment

.map(new Func1<T, T>() {
    @Override
    public T call(T t) {
        return null;
    }

is the one I add because I want to try changing the contents of T into something else, perhaps into another object. How do I achieve that? Before that, this is my understanding of the .map function in RxJava.

According to the official documentation at http://reactivex.io/RxJava/javadoc/rx/Observable.html#map(rx.functions.Func1) it says,

public final <R> Observable<R> map(Func1<? super T,? extends R> func)
Returns an Observable that applies a specified function to each item emitted by the source Observable and emits the results of these function applications.

func - a function to apply to each item emitted by the Observable

Am I correct to interpret from the definition of func above, the first parameter (super T), should be the 'incoming' data type, and the second parameter (extends R) is the 'outgoing' data type, or the data type of the transformed item. Because that's what I see from most of the code examples. Eg, from http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

Observable.just("Hello, world!")
    .map(new Func1<String, Integer>() {
        @Override
        public Integer call(String s) {
            return s.hashCode();
        }
    })

My understanding from the code above is Func1 receives string and passes it to call(String s), and returns Integer, as defined in Func1<String, Integer>. So is my interpretation above will always be correct? Because from my first code, I need to change T into something else, but when I changed the 2nd parameter of .map new Func1 to anything else other than T, Android Studio will give me the "Incompatible types" error.

Community
  • 1
  • 1
imin
  • 4,504
  • 13
  • 56
  • 103
  • You've written a generic *method* here... The `` at the start of the method declaration shows that `T` is a type parameter. – Jon Skeet Sep 22 '15 at 19:21
  • This method is a `` parametrized. Imagine you have a `String` in place of `T` or any other class type. It is called generics in Java – MaxZoom Sep 22 '15 at 19:27

2 Answers2

2

So you're now dealing with generic types. What you have here is a generified method, so it will be able to makeObservables dealing with any type you pass to it at runtime. The <T> will be resolved according to the parameter passed in to the method.

Want to make an Observable that deals with Date? makeObservable(new Callable<Date>() {...}); An Observable of String? makeObservable(new Callable<String>() {...});

Note that since your code's observable stream doesn't yet transform the input data, it outputs an Observable<T> as well. Note also that you code cannot know in advance the methods that it could call, so it will be restricted in the possible transformations it can apply.

One of these transformations (aka map) could be to get the string representation of the T object.

And by the way, yes you are correct in the map function, T is the input type and R the output type, so applying such a map in your code would result in the return type of makeObservable being Observable<R> (replace R by a concrete type, you cannot generify here).

public static <T> Observable<String> makeToStringObservable(final Callable<T> func) {
    return Observable.create(
            new Observable.OnSubscribe<T>() {
                @Override
                public void call(Subscriber<? super T> subscriber) {
                    try {
                        T observed = func.call();
                        if (observed != null) { // to make defaultIfEmpty work
                            subscriber.onNext(observed);
                        }
                        subscriber.onCompleted();
                    } catch (Exception ex) {
                        subscriber.onError(ex);
                    }
                }
            }).map(new Func1<T, String>() {
                @Override
                public String call(T t) {
                  //toString() is a method that is always there, yay
                  return t.toString();
            }
    });
Simon Baslé
  • 27,105
  • 5
  • 69
  • 70
1

T is your generic type. This link should help explain it: https://docs.oracle.com/javase/tutorial/java/generics/types.html

Basically the type will be replaced with a real type at runtime, for example String, or Object.

If you change your return type of Func from type T to something else, then you also must modify the Observable generic type. this can be seen in the definition of map: the Observable type R is consistent with the Func return type R.

bryanjj
  • 53
  • 6
  • Yup I've read the link you gave before posting the question. I still don't get it after going through the link, but I understand more when you mentioned "Basically the type will be replaced with a real type at runtime, for example String, or Object. ". And the "why?" use it has been answered here https://docs.oracle.com/javase/tutorial/java/generics/why.html. But can you explain more about modifying the Observable generic type? I still don't get it. – imin Sep 22 '15 at 19:35
  • the map function is defined as: public final Observable map(Func1 super T,? extends R> func) what you have implemented is: Observable map(Func1 func) so if you want to change the 2nd argument from T to something else, for example R, the Observable type must change as well, because that is how it is defined. – bryanjj Sep 22 '15 at 19:56
  • I understood that the Observable type must change as well, but sorry I still don't know how to implement it in my code above.. – imin Sep 22 '15 at 20:34