0

I've read some other questions on this topic , but my case is different as it encompasses three java classes.

First I've an adapter for recycler view which is sending me the name of the course clicked through an intent in onClick().

CustomAdapter.java:

import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

import static androidx.core.content.ContextCompat.startActivity;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private List<String> data;
    private List<Integer> d2;
    private Context c;
    public CustomAdapter (Context c , List<String> data,List<Integer> data2){
        this.c = c;
        this.data = data;
        this.d2 = data2;
    }

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View rowItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_view, parent, false);
        return new ViewHolder(rowItem);
    }

    @Override
    public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position) {
        holder.textView.setText(this.data.get(position));
        holder.tv2.setText(Integer.toString(this.d2.get(position)));

    }

    @Override
    public int getItemCount() {
        return this.data.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private TextView textView,tv2;

        public ViewHolder(View view) {
            super(view);
            view.setOnClickListener(this);
            this.textView = view.findViewById(R.id.textview);
            this.tv2 = view.findViewById(R.id.textview2);
        }

        @Override
        public void onClick(View view) {
// I'm passing the name of the course which the user clicked at to CourseList.java
            Intent i = new Intent(c, CourseList.class);
            i.putExtra("course",textView.getText().toString());
            c.startActivity(i);
        }
    }
}

Just to reiterate , here is the intent:

  @Override
        public void onClick(View view) {
// I'm passing the name of the course which the user clicked at to CourseList.java
            Intent i = new Intent(c, CourseList.class);
            i.putExtra("course",textView.getText().toString());
            c.startActivity(i);
        }

In CourseList.java, I want to show the topics related to the clicked course. So, making use of recyclerview and storing the topics related to each course (List<String>) in a HashMap<String,String[]>.

CourseList.java:

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class CourseList extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle saved){
        super.onCreate(saved);
        setContentView(R.layout.activity_reg);
        Bundle extras = getIntent().getExtras();
        HashMap courses = new HashMap<String,String[]>();
        courses.put("Negotiation",new String[]{"Common ground", "Carpet"});
        courses.put("Pyschology",new String[]{"Happy", "No"});
        courses.put("Joke",new String[]{"Map", "Sarcasm"});
        RecyclerView recyclerView = findViewById(R.id.recycler_view);
        String a = extras.getString("course");
        List<String> b = (List<String>) courses.get(a);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(new CourseAdapter(CourseList.this, b));
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));


    }
}

My HashMap courses.get() returns a java.lang.Object but the constructor of CourseAdapter.java requires a java.util.List<java.lang.String>. So, I'm typecasting at this line:

List<String> b = (List<String>) courses.get(a);

But my app is crashing with the runtime error:

java.lang.ClassCastException: java.lang.String[] cannot be cast to java.util.List

Here is CourseAdapter.java (Adapter for CourseList.java):

CourseAdapter.java:

import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

import static androidx.core.content.ContextCompat.startActivity;

public class CourseAdapter extends RecyclerView.Adapter<CourseAdapter.ViewHolder> {
    private List<String> data;
    private Context c;
    public CourseAdapter (Context c , List<String> data){
        this.c = c;
        this.data = data;
    }

    @Override
    public CourseAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View rowItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_view, parent, false);
        return new ViewHolder(rowItem);
    }

    @Override
    public void onBindViewHolder(CourseAdapter.ViewHolder holder, int position) {
        holder.textView.setText(this.data.get(position));

    }

    @Override
    public int getItemCount() {
        return this.data.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private TextView textView,tv2;

        public ViewHolder(View view) {
            super(view);
            view.setOnClickListener(this);
            this.textView = view.findViewById(R.id.textview);
        }

        @Override
        public void onClick(View view) {
            Intent i = new Intent(c, RegistrationActivity.class);
            c.startActivity(i);
        }
    }
}
Ken Adams
  • 83
  • 2
  • 9
  • this is a bad approach, why are you passing in context and handling click listeners inside of your recycler ? you should rather be using callback methods to move this logic out, back into your activity or fragment – a_local_nobody Feb 04 '22 at 12:26
  • @a_local_nobody how will I assign clickListeners for every element outside my recycler view? I think it will be too complicated. – Ken Adams Feb 04 '22 at 12:30
  • 1
    `I think it will be too complicated` what's normal for the spider is chaos for the fly :) lots of examples online for doing it, it's the better approach to follow , your approach doesn't make this recycler reusable https://stackoverflow.com/questions/30487700/recyclerview-onitemclicked-callback – a_local_nobody Feb 04 '22 at 12:37
  • 2
    Just to comment on the problem you have asked about (and not the whole approach). You are invoking `courses.get(a)` which will return a `String[]` and you are trying to cast it as a `List`. You can't just cast it as a `List` (which is what the exception says) and hope it works. Either have `List` in your `courses` map or convert the result of the get. – DanielBarbarian Feb 04 '22 at 12:51
  • 1
    @KenAdams You can just get the context from the view parameter `view.getContext()` – cmak Feb 04 '22 at 13:01

2 Answers2

1

... how can you NOT see it!?

    HashMap courses = new HashMap<String,String[]>();
    List<String> b = (List<String>) courses.get(a);

Let me show some detail what this does:

    HashMap courses = new HashMap<String,String[]>();
    String[] temp = courses.get(a);
    List<String> b = (List<String>) temp;

Now why does a cast from String[] to List<String> fail? Because one is a List, and the other one is an Array.

Either use

    HashMap<String,List<String>> courses = new HashMap<>();
    List<String> b = courses.get(a);

or do

    HashMap<String,String[]> courses = new HashMap<>();
    String[] b =  courses.get(a);

, no casting necessary.

JayC667
  • 2,418
  • 2
  • 17
  • 31
  • courses.get(a) is returning a `java.lang.Object`. So for your first suggestion , I'm getting this error: `Incompatible types. Found: 'java.lang.Object', required: 'java.util.List'`. Similar error for the second one as well. – Ken Adams Feb 04 '22 at 13:03
  • 1
    The most important thing to point out is that `HashMap courses` is a [raw type](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it). If it was correctly declared as `HashMap courses`, the code would simply not compile on the line where it is currently failing at runtime, because casting from a `String[]` to a `List` is not allowed. – Andy Turner Feb 04 '22 at 13:03
  • @KenAdams "I'm getting this error" because you're using raw types. – Andy Turner Feb 04 '22 at 13:05
  • Oh damn. I updated my code to properly reflect what I was suggesting. Thanx for the hints. I simply missed it when copying the code together. An NO, `courses.get(a)` will NOT return an `Object`. It is just your use of a raw type that will lead to that, forcing the compiler to assume it's an `Object`, because it cannot guarantee anything else. In any case, it will return what was stored in it. I updated my answer to show how it is done. – JayC667 Feb 04 '22 at 13:27
1

A type safe courses looks like:

Map<String, String[]> courses = new HashMap<>();

If you do not use parameters <> then the compiler switches off the generic typing, as ancient pre-generics java style.

As courses.get(a) returns a String[], you can shovel them into the list as:

List<String> b = new ArrayList<>();
Collections.addAll(b, courses.get(a));

Alternatively and shorter, but the list wraps the array, so you cannot add/remove, and set will alter the array (I understood you just want to iterate over it):

List<String> b = Arrays.asList(courses.get(a));

This also shows why you should use interfaces (List, Map) instead of implementing classes (ArrayList, HashMap) - as you did with HashMap -. as asList returns just a List<String>.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    "Alternatively and shorter:" but slightly different, insofar as changes to the second `b` will change the array stored in the map, whereas changes to the first `b` would not (amongst other differences). – Andy Turner Feb 04 '22 at 13:06