Java 表达式的计算结果可能为null,但由声明为@NotNull的方法返回
创建RecyclerView适配器后,将返回有关my view holder的警告。我已经阅读并理解了一点,但不清楚返回viewHolder中的viewHolder实例是什么;应替换为 表达式的计算结果可能为null,但由声明为@NotNull的方法返回Java 表达式的计算结果可能为null,但由声明为@NotNull的方法返回,java,android,android-studio,Java,Android,Android Studio,创建RecyclerView适配器后,将返回有关my view holder的警告。我已经阅读并理解了一点,但不清楚返回viewHolder中的viewHolder实例是什么;应替换为 表达式的计算结果可能为null,但由声明为@NotNull的方法返回 @NonNull只是一个IDE标志,用于Kotlin交叉兼容性。它所做的仅仅是告诉IDE它应该和不应该警告您潜在的空指针 声明它并不意味着方法将返回非null值。当然,你的将只是因为它的工作方式,但IDE不是那么聪明。它所看到的只是一个矛盾:使
@NonNull只是一个IDE标志,用于Kotlin交叉兼容性。它所做的仅仅是告诉IDE它应该和不应该警告您潜在的空指针 声明它并不意味着方法将返回非null值。当然,你的将只是因为它的工作方式,但IDE不是那么聪明。它所看到的只是一个矛盾:使用@NonNull参数,但也有返回null值的情况 解决此问题的最简单方法是将您的默认案例替换为TYPE_EXPANDABLE或TYPE_NONEXPANDABLE下的案例,并删除冗余案例,即: 编辑:根据Ben p的评论,如果当前视图类型是意外的,那么在OnCreateViewHolder方法中抛出异常也是一个好主意
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。。。您甚至可以抛出异常。您有这个
@NonNull
在onCreateViewHolder之前。警告消息清楚地告诉您,您需要确保返回的内容不是空的。在默认情况下,viewHolder为null。实际上,您可能希望返回一个有效值,或者在这种情况下引发异常。由于方法
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
不能返回null,但在默认情况下,您将返回null
default:
viewHolder = null;
break;
在生产中,默认情况将导致崩溃,这就是为什么会发出警告。
您必须抛出运行时异常或返回默认的非空视图 我喜欢@TheLoverter的答案,但我想再添加一个,以突出我认为会让你的生活更轻松的东西 与ListView不同,RecyclerView不关心从getItemViewType返回什么值;只要您从这个方法返回不同的值,RecyclerView就会很高兴。这允许您使用布局资源ID作为返回值,这意味着您永远不必定义自己的常量
@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.cardwiew\u a,而不是类型\u EXPANDABLE;两者都是隐藏的INT,但现在我们可以让资源框架为我们定义它们
如果我们碰到的对象不是电话呼叫或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);
}
}
因为我们从getItemViewType返回布局ID,所以我们不必担心从我们自己的常量转换为R.layout值;我们可以直接将viewType充气。不过,我们仍然需要switch语句来知道返回哪种类型的ViewHolder
同样,我们在这里抛出了一个异常,以防传递了一个我们不期望的viewType。这种情况不会发生,但如果真的发生了,问题所在就会非常清楚,并且很容易解决
@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);
}
}
这里唯一的区别是将常量替换为交换机内部的布局ID。没什么大不了的。当然,如果我们得到了意想不到的东西,我们就会崩溃
还要注意,我已经从参数中删除了final关键字。即使编译器很乐意让您添加它们,您也不应该这样做。由于像notifyItemInserted这样的方法,视图持有者的位置可能会随着时间的推移而改变,而不会被重新绑定。处理这个问题超出了您的问题范围,但需要指出。我同意大部分答案。我特别喜欢在getItemViewType的末尾抛出,尽管我个人会选择IllegalStateException或AssertionError。。。但我想说的是,如果viewType参数是您不期望的,那么您还应该加入onCreateViewHolder。如果你通过了第三个你不希望的选项,那么你的应用程序崩溃会更好,也许你以后会更改getItemViewType,但忘记更改onCreateViewHolder,而不是返回错误的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);
}
}