Android-检查非空的Isn';他没有按预期工作

Android-检查非空的Isn';他没有按预期工作,android,nullpointerexception,kotlin,Android,Nullpointerexception,Kotlin,我遇到了一个奇怪的问题,我对NOTNULL的检查不起作用。我试图检查值是否为空,如果为空,会弹出一个警告,上面写着“配料需要一个名称”,我不知道为什么,但我似乎无法弄清楚这应该是相当简单的 我对Kotlin还是新手,所以我怀疑我的Java到Kotlin的思维过程在这里有缺陷 任何帮助和建议都将不胜感激 我遇到问题的部分是: // Check that the name is not null (if (values != null) values else throw KotlinNul

我遇到了一个奇怪的问题,我对NOTNULL的检查不起作用。我试图检查值是否为空,如果为空,会弹出一个警告,上面写着“配料需要一个名称”,我不知道为什么,但我似乎无法弄清楚这应该是相当简单的

我对Kotlin还是新手,所以我怀疑我的Java到Kotlin的思维过程在这里有缺陷

任何帮助和建议都将不胜感激

我遇到问题的部分是:

// Check that the name is not null
    (if (values != null) values else throw KotlinNullPointerException()).getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME) ?: throw IllegalArgumentException("Ingredient requires a name")
全班同学。我倾向于评论所有的垃圾,希望这是有意义的:

import android.content.ContentProvider
import android.content.ContentUris
import android.content.ContentValues
import android.content.UriMatcher
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import android.util.Log
import 
import kotlin.properties.Delegates

class DbContentProvider : ContentProvider() {

// Database helper object
private var mDbHelper: DbHelper by Delegates.notNull()


// Order the of ingredients in the list view
private val ingredientSortBy = IngredientEntry.COLUMN_INGREDIENT_NAME

override fun onCreate(): Boolean {
    mDbHelper = DbHelper(context)
    return true
}

override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?,
                   sortOrder: String?): Cursor? {

    // Get readable database
    val database: SQLiteDatabase = mDbHelper.readableDatabase

    // This cursor will hold the result of the query
    val cursor: Cursor

    // Figure out if the URI matcher can match the URI to a specific code
    val match = sUriMatcher.match(uri)
    when (match) {
        INGREDIENTS ->
            // For the INGREDIENTS code, query the ingredients table directly with the given
            // projection, selection, selection arguments, and sort order. The cursor
            // could contain multiple rows of the ingredients table.
            // Query is uri, projection, selection, selectionArgs, sortOrder and orderBy
            cursor = database.query(IngredientEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, ingredientSortBy)
        INGREDIENTS_ID -> {
            // For the INGREDIENTS_ID code, extract out the ID from the URI.
            // For an example URI such as "content://com.example.android.ingredients/ingredients/3",
            // the selection will be "_id=?" and the selection argument will be a
            // String array containing the actual ID of 3 in this case.
            //
            // For every "?" in the selection, we need to have an element in the selection
            // arguments that will fill in the "?". Since we have 1 question mark in the
            // selection, we have 1 String in the selection arguments' String array.
            val mSelection = IngredientEntry._ID + "=?"
            val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())

            // This will perform a query on the ingredients table where the _id equals 3 to return a
            // Cursor containing that row of the table.
            // Query is uri, projection, selection, selectionArgs, sortOrder and orderBy
            cursor = database.query(IngredientEntry.TABLE_NAME, projection, mSelection, mSelectionArgs, null, null, ingredientSortBy)
        }
        else -> throw IllegalArgumentException("Cannot query unknown URI " + uri)
    }

    // Set notification URI on the Cursor,
    // so we know what content URI the Cursor was created for.
    // If the data at this URI changes, then we know we need to update the Cursor.
    cursor.setNotificationUri(context.contentResolver, uri)

    // Return the cursor
    return cursor
}

override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
    val match = sUriMatcher.match(uri)
    when (match) {
        INGREDIENTS -> return insertIngredient(uri, contentValues)
        else -> throw IllegalArgumentException("Insertion is not supported for " + uri)
    }
}

/**
 * Insert an ingredient into the database with the given content values. Return the new content URI
 * for that specific row in the database.
 */
private fun insertIngredient(uri: Uri, contentValues: ContentValues?): Uri? {
    // Check that the name is not null or empty
    val ingredientName = contentValues?.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME)
    if (ingredientName == null || ingredientName.isEmpty()) {
        throw IllegalArgumentException("Ingredient requires valid category")
    }

    // Check that the category is valid
    val category = contentValues.getAsInteger(IngredientEntry.COLUMN_INGREDIENT_CATEGORY)
    if (category == null || !IngredientEntry.isValidCategory(category)) {
        throw IllegalArgumentException("Ingredient requires valid category")
    }

    // Check that the measurement is valid
    val measurement = contentValues.getAsInteger(IngredientEntry.COLUMN_INGREDIENT_MEASUREMENT)
    if (measurement == null || !IngredientEntry.isValidMeasurement(measurement)) {
        throw IllegalArgumentException("Ingredient requires valid measurement")
    }

    // No need to check the description, any value is valid (including null).

    // Get writable database
    val database = mDbHelper.writableDatabase

    // Insert the new ingredient with the given values
    val id = database.insert(IngredientEntry.TABLE_NAME, null, contentValues)
    // If the ID is -1, then the insertion failed. Log an error and return null.
    if (id == (-1).toLong()) {
        Log.e(LOG_TAG, "Failed to insert row for " + uri)
        return null
    }

    // Notify all listeners that the data has changed for the ingredient content URI
    context.contentResolver.notifyChange(uri, null)

    // Return the new URI with the ID (of the newly inserted row) appended at the end
    return ContentUris.withAppendedId(uri, id)
}

override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
                    selectionArgs: Array<String>?): Int {

    val match = sUriMatcher.match(uri)
    return when (match) {
        INGREDIENTS -> updateIngredient(uri, contentValues, selection, selectionArgs)
        INGREDIENTS_ID -> {
            // For the INGREDIENTS_ID code, extract out the ID from the URI,
            // so we know which row to update. Selection will be "_id=?" and selection
            // arguments will be a String array containing the actual ID.
            val mSelection = IngredientEntry._ID + "=?"
            val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())
            updateIngredient(uri, contentValues, mSelection, mSelectionArgs)
        }
        else -> throw IllegalArgumentException("Update is not supported for " + uri)
    }
}

/**
 * Update ingredients in the database with the given content contentValues. Apply the changes to the rows
 * specified in the selection and selection arguments (which could be 0 or 1 or more ingredients).
 * Return the number of rows that were successfully updated.
 */
private fun updateIngredient(uri: Uri, contentValues: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
    // If the {@link IngredientEntry#COLUMN_INGREDIENT_NAME} key is present,
    // check that the name value is not null.
    if ((if (contentValues != null) contentValues else throw KotlinNullPointerException()).containsKey(IngredientEntry.COLUMN_INGREDIENT_NAME)) {
        contentValues.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME) ?: throw IllegalArgumentException("Ingredient requires a name")
    }

    // If the {@link IngredientEntry#COLUMN_INGREDIENT_CATEGORY} key is present,
    // check that the category value is valid.
    if (contentValues.containsKey(IngredientEntry.COLUMN_INGREDIENT_CATEGORY)) {
        val category = contentValues.getAsInteger(IngredientEntry.COLUMN_INGREDIENT_CATEGORY)
        if (category == null || !IngredientEntry.isValidCategory(category)) {
            throw IllegalArgumentException("Ingredient requires valid category")
        }
    }

    // If the {@link IngredientEntry#COLUMN_INGREDIENT_MEASUREMENT} key is present,
    // check that the measurement value is valid.
    if (contentValues.containsKey(IngredientEntry.COLUMN_INGREDIENT_MEASUREMENT)) {
        val measurement = contentValues.getAsInteger(IngredientEntry.COLUMN_INGREDIENT_MEASUREMENT)
        if (measurement == null || !IngredientEntry.isValidMeasurement(measurement)) {
            throw IllegalArgumentException("Ingredient requires valid measurement")
        }
    }

    // No need to check the description, any value is valid (including null).

    // If there are no contentValues to update, then don't try to update the database
    if (contentValues.size() == 0) {
        return 0
    }

    // Otherwise, get writable database to update the data
    val database = mDbHelper.writableDatabase

    // Perform the update on the database and get the number of rows affected
    val rowsUpdated = database.update(IngredientEntry.TABLE_NAME, contentValues, selection, selectionArgs)

    // If 1 or more rows were updated, then notify all listeners that the data at the
    // given URI has changed
    if (rowsUpdated != 0) {
        context.contentResolver.notifyChange(uri, null)
    }

    // Return the number of rows updated
    return rowsUpdated
}

override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {

    // Get writable database
    val database = mDbHelper.writableDatabase

    // Track the number of rows that were deleted
    val rowsDeleted: Int

    val match = sUriMatcher.match(uri)
    rowsDeleted = when (match) {
        INGREDIENTS ->
            // Delete all rows that match the selection and selection args
            database.delete(IngredientEntry.TABLE_NAME, selection, selectionArgs)
        INGREDIENTS_ID -> {
            // Delete a single row given by the ID in the URI
            val mSelection = IngredientEntry._ID + "=?"
            val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())
            database.delete(IngredientEntry.TABLE_NAME, mSelection, mSelectionArgs)
        }
        else -> throw IllegalArgumentException("Deletion is not supported for " + uri)
    }

    // If 1 or more rows were deleted, then notify all listeners that the data at the
    // given URI has changed
    if (rowsDeleted != 0) {
        context.contentResolver.notifyChange(uri, null)
    }

    // Return the number of rows deleted
    return rowsDeleted
}

override fun getType(uri: Uri): String? {
    val match = sUriMatcher.match(uri)
    return when (match) {
        INGREDIENTS -> IngredientEntry.CONTENT_LIST_TYPE
        INGREDIENTS_ID -> IngredientEntry.CONTENT_ITEM_TYPE
        else -> throw IllegalStateException("Unknown URI $uri with match $match")
    }
}

companion object {

    /** Tag for the log messages  */
    val LOG_TAG: String? = ContentProvider::class.java.simpleName

    /** URI matcher code for the content URI for the ingredients table  */
    private val INGREDIENTS = 100

    /** URI matcher code for the content URI for a single ingredient in the ingredients table  */
    private val INGREDIENTS_ID = 101

    /**
     * UriMatcher object to match a content URI to a corresponding code.
     * The input passed into the constructor represents the code to return for the root URI.
     * It's common to use NO_MATCH as the input for this case.
     */
    private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH)

    // Static initializer. This is run the first time anything is called from this class.
    init {
        // The calls to addURI() go here, for all of the content URI patterns that the provider
        // should recognize. All paths added to the UriMatcher have a corresponding code to return
        // when a match is found.

        // The content URI of the form "content://com.example.android.ingredients/ingredients" will map to the
        // integer code {@link #INGREDIENTS}. This URI is used to provide access to MULTIPLE rows
        // of the ingredients table.
        sUriMatcher.addURI(DbContract.CONTENT_AUTHORITY, DbContract.PATH_INGREDIENTS, INGREDIENTS)

        // The content URI of the form "content://com.example.android.ingredients/ingredients/#" will map to the
        // integer code {@link #INGREDIENTS_ID}. This URI is used to provide access to ONE single row
        // of the ingredients table.
        //
        // In this case, the "#" wildcard is used where "#" can be substituted for an integer.
        // For example, "content://com.example.android.ingredients/ingredients/3" matches, but
        // "content://com.example.android.ingredients/ingredients" (without a number at the end) doesn't match.
        sUriMatcher.addURI(DbContract.CONTENT_AUTHORITY, DbContract.PATH_INGREDIENTS + "/#", INGREDIENTS_ID)
    }
}
}
我制作的另一个版本捕捉到了异常,不会使应用程序崩溃,但仍然允许用户创建一种没有名称的配料。呸

// Check that the name EditText field is not null or empty
    val ingredientName = contentValues?.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME)
    if (ingredientName == null || ingredientName.isEmpty()) {
        try {
            throw Exception ("Ingredient requires a name")
        } catch (exception: Exception) {
            System.out.println(exception)
        }
    }

您可以省略if语句,改为执行此操作

values?.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME) ?: throw IllegalArgumentException("Ingredient requires a name")
elvis运算符不起作用,因为if语句从不返回null,它是字符串或NPE

实际上,使用let block可以使代码变得更好。像这样的

values?.let{
    //logic here that uses value, refer to value as it
} ?: IllegalArgumentException()
编辑

values?.let{
    if(it.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME).isEmpty()) throw IllegalArgumentException()
    else // do your logic
}

您可以省略if语句,改为执行此操作

values?.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME) ?: throw IllegalArgumentException("Ingredient requires a name")
elvis运算符不起作用,因为if语句从不返回null,它是字符串或NPE

实际上,使用let block可以使代码变得更好。像这样的

values?.let{
    //logic here that uses value, refer to value as it
} ?: IllegalArgumentException()
编辑

values?.let{
    if(it.getAsString(IngredientEntry.COLUMN_INGREDIENT_NAME).isEmpty()) throw IllegalArgumentException()
    else // do your logic
}

非常感谢。我试过你的建议,但没有效果。仍然允许我添加一个没有名称的成分。可能是值从来都不是空的。您可以使用
.isEmpty()
检查是否为空。请检查编辑您可能是对的。我会尝试一下,稍后再查看。
getAsString()
是否返回
字符串?
字符串
?如果返回NULL,则可能需要考虑NULL,如果它返回了代码> String?扩展IF条件以满足两个代码<空> < /代码>和<代码> null < /代码>。isEmpty也不起作用。我认为价值观是在另一项活动中定义的,这就是为什么它在这里不起作用。我可能会完全取消名称检查,尝试将TextChanged侦听器添加到我的EditText框中,并在其中一个覆盖方法中添加字段检查逻辑。关键是要确保用户在更新我的应用程序或在我的应用程序中添加某个成分之前,不能将“名称”字段留空,这样可能就是罚单。谢谢。我试过你的建议,但没有效果。仍然允许我添加一个没有名称的成分。可能是值从来都不是空的。您可以使用
.isEmpty()
检查是否为空。请检查编辑您可能是对的。我会尝试一下,稍后再查看。
getAsString()
是否返回
字符串?
字符串
?如果返回NULL,则可能需要考虑NULL,如果它返回了代码> String?扩展IF条件以满足两个代码<空> < /代码>和<代码> null < /代码>。isEmpty也不起作用。我认为价值观是在另一项活动中定义的,这就是为什么它在这里不起作用。我可能会完全取消名称检查,尝试将TextChanged侦听器添加到我的EditText框中,并在其中一个覆盖方法中添加字段检查逻辑。关键是要确保用户在更新或添加我的应用程序中的成分之前,不能将名称字段留空,因为这可能是罚单。您能否演示如何调用
趣味插入(uri:uri,contentValues:contentValues?):Uri?
method?该方法是必需的重写,在我的应用程序中没有在任何其他地方使用。您能否演示如何调用
fun insert(Uri:Uri,contentValues:contentValues?):Uri?
方法?该方法是必需的重写,在我的应用程序中没有在任何其他地方使用。