代码之家  ›  专栏  ›  技术社区  ›  wbk727

表达式的计算结果可能为null,但由声明为@NotNull的方法返回

  •  0
  • wbk727  · 技术社区  · 6 年前

    创建RecyclerView适配器后,将返回有关my view holder的警告。我读过 this question 有点理解,但不清楚 viewHolder return viewHolder; 应替换为。

    表达式的计算结果可能为null,但由声明为@NotNull的方法返回

    public class MyRVAapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        private final static int TYPE_EXPANDABLE = 1, TYPE_NONEXPANDABLE = 2;
        private ArrayList callSMSFeed = new ArrayList();
        private Context context;
    
        public MyRVAapter(Context context){
            this.context = context;
        }
    
        public void setCallSMSFeed(List<Object> callSMSFeed){
            this.callSMSFeed = (ArrayList) callSMSFeed;
        }
    
      @Override
        public int getItemViewType(int position) {
            if (callSMSFeed.get(position) instanceof Phonecall) {
                return TYPE_EXPANDABLE;
            } else if (callSMSFeed.get(position) instanceof SMSmessage) {
                return TYPE_NONEXPANDABLE;
            }
            return -1;
        }
    
        @Override
        public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
            int viewType=holder.getItemViewType();
            switch (viewType){
                case TYPE_EXPANDABLE:
                    Phonecall call = (Phonecall) callSMSFeed.get(position);
                    ((CallViewHolder)holder).showCallDetails(call);
                    break;
                case TYPE_NONEXPANDABLE:
                    SMSmessage sms = (SMSmessage)callSMSFeed.get(position);
                    ((SMSViewHolder)holder).showSmsDetails(sms);
                    break;
            }
        }
    
        @Override
        public int getItemCount(){return callSMSFeed.size();}
    
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
            int layout;
    
            RecyclerView.ViewHolder viewHolder;
            switch (viewType){
                case TYPE_EXPANDABLE:
                    layout = R.layout.cardview_a;
                    View callsView = LayoutInflater
                            .from(parent.getContext())
                            .inflate(layout, parent, false);
                    viewHolder = new CallViewHolder(callsView);
                    break;
                case TYPE_NONEXPANDABLE:
                    layout = R.layout.cardview_b;
                    View smsView = LayoutInflater
                            .from(parent.getContext())
                            .inflate(layout, parent, false);
                    viewHolder = new SMSViewHolder(smsView);
                    break;
                default:
                    viewHolder = null;
                    break;
            }
            return viewHolder;
        }
    }
    
    4 回复  |  直到 6 年前
        1
  •  5
  •   Ben P.    6 年前

    我喜欢@TheWander的答案,但我想再加一个,以突出一些我认为会让你的生活更轻松的东西。

    不像 ListView , RecyclerView 不管你的价值观是什么 getItemViewType()

    @Override
    public int getItemViewType(int position) {
        Object obj = callSMSFeed.get(position);
    
        if (obj instanceof Phonecall) {
            return R.layout.cardview_a;
        } else if (obj instanceof SMSmessage) {
            return R.layout.cardview_b;
        }
    
        throw new IllegalStateException("item at position " + position + " is not a Phonecall or SMSmessage: " + obj);
    }
    

    我们回来了 R.layout.cardview_a 而不是 TYPE_EXPANDABLE int 现在我们可以让资源框架为我们定义它们。

    如果遇到异常,我们也会抛出异常 Object 那可不是个好主意 Phonecall SMSmessage 这样我们马上就知道我们需要处理一个案子。这种例外应该只被犯错误的开发人员看到;应用程序永远不会在用户身上崩溃,因为你会在发布应用程序之前修复崩溃。

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View itemView = inflater.inflate(viewType, parent, false);
    
        switch (viewType) {
            case R.layout.cardview_a:
                return new CallViewHolder(itemView);
    
            case R.layout.cardview_b:
                return new SMSViewHolder(itemView);
    
            default:
                throw new IllegalArgumentException("unexpected viewType: " + viewType);
        }
    }
    

    因为我们从 R.layout 价值观;我们可以把车充气 viewType switch ViewHolder 返回。

    视图类型 我们没想到。这“不会发生”,但如果真的发生了 ,它将非常清楚问题所在,并且便于您解决。

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        int viewType = holder.getItemViewType();
    
        switch (viewType) {
            case R.layout.cardview_a:
                Phonecall call = (Phonecall) callSMSFeed.get(position);
                ((CallViewHolder) holder).showCallDetails(call);
                break;
    
            case R.layout.cardview_b:
                SMSmessage sms = (SMSmessage) callSMSFeed.get(position);
                ((SMSViewHolder) holder).showSmsDetails(sms);
                break;
    
            default:
                throw new IllegalArgumentException("unexpected viewType: " + viewType);
        }
    }
    

    这里唯一的区别是将常量替换为 转换 . 没什么大不了的。当然,如果我们得到了意想不到的东西,我们就会崩溃。

    final 参数中的关键字。即使编译器很乐意让您添加它们,您也不应该这样做。因为像这样的方法 notifyItemInserted() 取景器 的位置会随着时间的推移而改变,而不会被重新绑定。处理这件事超出了你的问题范围,但需要指出。

        2
  •  4
  •   TheWanderer    6 年前

    @NonNull只是一个IDE标志(用于Kotlin交叉兼容性)。它所做的仅仅是告诉IDE它应该什么时候警告您,也不应该警告您潜在的空指针。

    声明它并不意味着方法将返回非空值。当然,你的将只是因为它的工作方式,但IDE不是那么聪明。它所看到的只是一个矛盾:使用@NonNull参数,但也有返回null值的情况。

    解决这个问题最简单的方法就是更换 default TYPE_EXPANDABLE TYPE_NONEXPANDABLE 并移除多余的案例,即:

    onCreateViewHolderMethod()

    switch (viewType){
        case TYPE_EXPANDABLE:
            layout = R.layout.cardview_a;
            View callsView = LayoutInflater
                    .from(parent.getContext())
                    .inflate(layout, parent, false);
            viewHolder = new CallViewHolder(callsView);
            break;
        case TYPE_NONEXPANDABLE:
            layout = R.layout.cardview_b;
            View smsView = LayoutInflater
                    .from(parent.getContext())
                    .inflate(layout, parent, false);
            viewHolder = new SMSViewHolder(smsView);
            break;
        default:
            throw IllegalArgumentException("Invalid View type: " + viewType);
    }
    

    这不是最优雅的,但只要你确定 getItemViewType() -1 ,你不会遇到任何奇怪的行为。

    不过,我还要补充一点,这样你就可以确定你会得到你想要的。而不是这样:

    @Override
    public int getItemViewType(int position) {
        if (callSMSFeed.get(position) instanceof Phonecall) {
            return TYPE_EXPANDABLE;
        } else if (callSMSFeed.get(position) instanceof SMSmessage) {
            return TYPE_NONEXPANDABLE;
        }
        return -1;
    }
    

    试试这个:

    @Override
    public int getItemViewType(int position) {
        if (callSMSFeed.get(position) instanceof Phonecall) {
            return TYPE_EXPANDABLE;
        } else if (callSMSFeed.get(position) instanceof SMSmessage) {
            return TYPE_NONEXPANDABLE;
        }
        throw new IllegalArgumentException("Item at position " + position + " is not an instance of either Phonecall or SMSmessage");
    }
    

    注意:您可以使用您认为适合该案例的任何异常类型。我正在使用IllegalArgumentException,Ben P(来自评论)可能会使用IllegalStateException。。。你甚至可以抛出异常。

        3
  •  0
  •   Lajos Arpad    6 年前

    你有这个

    @NonNull
    

    在你的 onCreateViewHolder null . 如果是 default , viewHolder 无效的 实际上,您可能希望返回一个有效值,或者在这种情况下抛出一个异常。

        4
  •  0
  •   Anis BEN NSIR    6 年前

    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    

    不能返回null,但在默认情况下返回null

    default:
                    viewHolder = null;
                    break;
    

    在生产中,默认情况将导致崩溃,这就是为什么会发出警告。 必须抛出运行时异常或返回默认的非空视图。