public abstract class

NavType<T>

extends java.lang.Object

 java.lang.Object

↳androidx.navigation.NavType<T>

Subclasses:

NavType.ParcelableType<D>, NavType.ParcelableArrayType<D>, NavType.SerializableType<D>, NavType.EnumType<D>, NavType.SerializableArrayType<D>

Overview

NavType denotes the type that can be used in a NavArgument.

There are built-in NavTypes for primitive types, such as int, long, boolean, float, and strings, parcelable, and serializable classes (including Enums), as well as arrays of each supported type.

You should only use one of the static NavType instances and subclasses defined in this class.

Summary

Fields
public static final NavType<UnknownReference>BoolArrayType

NavType for storing boolean arrays, corresponding with the "boolean[]" type in a Navigation XML file.

public static final NavType<java.lang.Boolean>BoolType

NavType for storing boolean values, corresponding with the "boolean" type in a Navigation XML file.

public static final NavType<UnknownReference>FloatArrayType

NavType for storing float arrays, corresponding with the "float[]" type in a Navigation XML file.

public static final NavType<java.lang.Float>FloatType

NavType for storing float values, corresponding with the "float" type in a Navigation XML file.

public static final NavType<UnknownReference>IntArrayType

NavType for storing integer arrays, corresponding with the "integer[]" type in a Navigation XML file.

public static final NavType<java.lang.Integer>IntType

NavType for storing integer values, corresponding with the "integer" type in a Navigation XML file.

public static final NavType<UnknownReference>LongArrayType

NavType for storing long arrays, corresponding with the "long[]" type in a Navigation XML file.

public static final NavType<java.lang.Long>LongType

NavType for storing long values, corresponding with the "long" type in a Navigation XML file.

public static final NavType<java.lang.Integer>ReferenceType

NavType for storing integer values representing resource ids, corresponding with the "reference" type in a Navigation XML file.

public static final NavType<java.lang.String>StringArrayType

NavType for storing String arrays, corresponding with the "string[]" type in a Navigation XML file.

public static final NavType<java.lang.String>StringType

NavType for storing String values, corresponding with the "string" type in a Navigation XML file.

Methods
public static NavType<java.lang.Object>fromArgType(java.lang.String type, java.lang.String packageName)

Parse an argType string into a NavType.

public abstract java.lang.Objectget(Bundle bundle, java.lang.String key)

Get a value of this type from the bundle

public abstract java.lang.StringgetName()

Returns the name of this type.

public booleanisNullableAllowed()

Check if an argument with this type can hold a null value.

public abstract java.lang.ObjectparseValue(java.lang.String value)

Parse a value of this type from a String.

public abstract voidput(Bundle bundle, java.lang.String key, java.lang.Object value)

Put a value of this type in he bundle

public java.lang.StringtoString()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public static final NavType<java.lang.Integer> IntType

NavType for storing integer values, corresponding with the "integer" type in a Navigation XML file.

Null values are not supported.

public static final NavType<java.lang.Integer> ReferenceType

NavType for storing integer values representing resource ids, corresponding with the "reference" type in a Navigation XML file.

Null values are not supported.

public static final NavType<UnknownReference> IntArrayType

NavType for storing integer arrays, corresponding with the "integer[]" type in a Navigation XML file.

Null values are supported. Default values in Navigation XML files are not supported.

public static final NavType<java.lang.Long> LongType

NavType for storing long values, corresponding with the "long" type in a Navigation XML file.

Null values are not supported. Default values for this type in Navigation XML files must always end with an 'L' suffix, e.g. `app:defaultValue="123L"`.

public static final NavType<UnknownReference> LongArrayType

NavType for storing long arrays, corresponding with the "long[]" type in a Navigation XML file.

Null values are supported. Default values in Navigation XML files are not supported.

public static final NavType<java.lang.Float> FloatType

NavType for storing float values, corresponding with the "float" type in a Navigation XML file.

Null values are not supported.

public static final NavType<UnknownReference> FloatArrayType

NavType for storing float arrays, corresponding with the "float[]" type in a Navigation XML file.

Null values are supported. Default values in Navigation XML files are not supported.

public static final NavType<java.lang.Boolean> BoolType

NavType for storing boolean values, corresponding with the "boolean" type in a Navigation XML file.

Null values are not supported.

public static final NavType<UnknownReference> BoolArrayType

NavType for storing boolean arrays, corresponding with the "boolean[]" type in a Navigation XML file.

Null values are supported. Default values in Navigation XML files are not supported.

public static final NavType<java.lang.String> StringType

NavType for storing String values, corresponding with the "string" type in a Navigation XML file.

Null values are supported.

public static final NavType<java.lang.String> StringArrayType

NavType for storing String arrays, corresponding with the "string[]" type in a Navigation XML file.

Null values are supported. Default values in Navigation XML files are not supported.

Methods

public boolean isNullableAllowed()

Check if an argument with this type can hold a null value.

Returns:

Returns true if this type allows null values, false otherwise.

public abstract void put(Bundle bundle, java.lang.String key, java.lang.Object value)

Put a value of this type in he bundle

Parameters:

bundle: bundle to put value in
key: bundle key
value: value of this type

public abstract java.lang.Object get(Bundle bundle, java.lang.String key)

Get a value of this type from the bundle

Parameters:

bundle: bundle to get value from
key: bundle key

Returns:

value of this type

public abstract java.lang.Object parseValue(java.lang.String value)

Parse a value of this type from a String.

Parameters:

value: string representation of a value of this type

Returns:

parsed value of the type represented by this NavType

public abstract java.lang.String getName()

Returns the name of this type.

This is the same value that is used in Navigation XML argType attribute.

Returns:

name of this type

public java.lang.String toString()

public static NavType<java.lang.Object> fromArgType(java.lang.String type, java.lang.String packageName)

Parse an argType string into a NavType.

Parameters:

type: argType string, usually parsed from the Navigation XML file
packageName: package name of the R file, used for parsing relative class names starting with a dot.

Returns:

a NavType representing the type indicated by the argType string. Defaults to StringType for null.

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.navigation;

import android.os.Bundle;
import android.os.Parcelable;

import androidx.annotation.AnyRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.Serializable;
import java.text.ParseException;

/**
 * NavType denotes the type that can be used in a {@link NavArgument}.
 * <p>
 * There are built-in NavTypes for primitive types, such as int, long, boolean, float, and
 * strings, parcelable, and serializable classes (including Enums), as well as arrays of
 * each supported type.
 * <p>
 * You should only use one of the static NavType instances and subclasses
 * defined in this class.
 *
 * @param <T> the type of the data that is supported by this NavType
 */
public abstract class NavType<T> {
    private final boolean mNullableAllowed;

    NavType(boolean nullableAllowed) {
        this.mNullableAllowed = nullableAllowed;
    }

    /**
     * Check if an argument with this type can hold a null value.
     * @return Returns true if this type allows null values, false otherwise.
     */
    public boolean isNullableAllowed() {
        return mNullableAllowed;
    }

    /**
     * Put a value of this type in he {@code bundle}
     *
     * @param bundle bundle to put value in
     * @param key    bundle key
     * @param value  value of this type
     */
    public abstract void put(@NonNull Bundle bundle, @NonNull String key, @Nullable T value);

    /**
     * Get a value of this type from the {@code bundle}
     *
     * @param bundle bundle to get value from
     * @param key    bundle key
     * @return value of this type
     */
    @Nullable
    public abstract T get(@NonNull Bundle bundle, @NonNull String key);

    /**
     * Parse a value of this type from a String.
     *
     * @param value string representation of a value of this type
     * @return parsed value of the type represented by this NavType
     * @throws IllegalArgumentException if value cannot be parsed into this type
     */
    @NonNull
    public abstract T parseValue(@NonNull String value);

    /**
     * Parse a value of this type from a String and put it in a {@code bundle}
     *
     * @param bundle bundle to put value in
     * @param key    bundle key under which to put the value
     * @param value  parsed value
     * @return parsed value of the type represented by this NavType
     * @throws ParseException if value cannot be parsed into this type
     */
    @NonNull
    T parseAndPut(@NonNull Bundle bundle, @NonNull String key, @NonNull String value) {
        T parsedValue = parseValue(value);
        put(bundle, key, parsedValue);
        return parsedValue;
    }

    /**
     * Returns the name of this type.
     * <p>
     * This is the same value that is used in Navigation XML <code>argType</code> attribute.
     *
     * @return name of this type
     */
    @NonNull
    public abstract String getName();

    @Override
    @NonNull
    public String toString() {
        return getName();
    }

    /**
     * Parse an argType string into a NavType.
     *
     * @param type        argType string, usually parsed from the Navigation XML file
     * @param packageName package name of the R file,
     *                    used for parsing relative class names starting with a dot.
     * @return a NavType representing the type indicated by the argType string.
     * Defaults to StringType for null.
     */
    @SuppressWarnings("unchecked")
    @NonNull
    public static NavType<?> fromArgType(@Nullable String type, @Nullable String packageName) {
        if (IntType.getName().equals(type)) {
            return IntType;
        } else if (IntArrayType.getName().equals(type)) {
            return IntArrayType;
        } else if (LongType.getName().equals(type)) {
            return LongType;
        } else if (LongArrayType.getName().equals(type)) {
            return LongArrayType;
        } else if (BoolType.getName().equals(type)) {
            return BoolType;
        } else if (BoolArrayType.getName().equals(type)) {
            return BoolArrayType;
        } else if (StringType.getName().equals(type)) {
            return StringType;
        } else if (StringArrayType.getName().equals(type)) {
            return StringArrayType;
        } else if (FloatType.getName().equals(type)) {
            return FloatType;
        } else if (FloatArrayType.getName().equals(type)) {
            return FloatArrayType;
        } else if (ReferenceType.getName().equals(type)) {
            return ReferenceType;
        } else if (type != null && !type.isEmpty()) {
            try {
                String className;
                if (type.startsWith(".") && packageName != null) {
                    className = packageName + type;
                } else {
                    className = type;
                }

                if (type.endsWith("[]")) {
                    className = className.substring(0, className.length() - 2);
                    Class<?> clazz = Class.forName(className);
                    if (Parcelable.class.isAssignableFrom(clazz)) {
                        return new ParcelableArrayType(clazz);
                    } else if (Serializable.class.isAssignableFrom(clazz)) {
                        return new SerializableArrayType(clazz);
                    }
                } else {
                    Class<?> clazz = Class.forName(className);
                    if (Parcelable.class.isAssignableFrom(clazz)) {
                        return new ParcelableType(clazz);
                    } else if (Enum.class.isAssignableFrom(clazz)) {
                        return new EnumType(clazz);
                    } else if (Serializable.class.isAssignableFrom(clazz)) {
                        return new SerializableType(clazz);
                    }
                }
                throw new IllegalArgumentException(className + " is not Serializable or "
                        + "Parcelable.");
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return StringType;
    }

    @NonNull
    static NavType inferFromValue(@NonNull String value) {
        //because we allow Long literals without the L suffix at runtime,
        //the order of IntType and LongType parsing has to be reversed compared to Safe Args
        try {
            IntType.parseValue(value);
            return IntType;
        } catch (IllegalArgumentException e) {
            //ignored, proceed to check next type
        }
        try {
            LongType.parseValue(value);
            return LongType;
        } catch (IllegalArgumentException e) {
            //ignored, proceed to check next type
        }

        try {
            FloatType.parseValue(value);
            return FloatType;
        } catch (IllegalArgumentException e) {
            //ignored, proceed to check next type
        }

        try {
            BoolType.parseValue(value);
            return BoolType;
        } catch (IllegalArgumentException e) {
            //ignored, proceed to check next type
        }

        return StringType;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    static NavType inferFromValueType(@Nullable Object value) {
        if (value instanceof Integer) {
            return IntType;
        } else if (value instanceof int[]) {
            return IntArrayType;
        } else if (value instanceof Long) {
            return LongType;
        } else if (value instanceof long[]) {
            return LongArrayType;
        } else if (value instanceof Float) {
            return FloatType;
        } else if (value instanceof float[]) {
            return FloatArrayType;
        } else if (value instanceof Boolean) {
            return BoolType;
        } else if (value instanceof boolean[]) {
            return BoolArrayType;
        } else if (value instanceof String || value == null) {
            return StringType;
        } else if (value instanceof String[]) {
            return StringArrayType;
        } else if (value.getClass().isArray()
                && Parcelable.class.isAssignableFrom(value.getClass().getComponentType())) {
            return new ParcelableArrayType(value.getClass().getComponentType());
        } else if (value.getClass().isArray()
                && Serializable.class.isAssignableFrom(value.getClass().getComponentType())) {
            return new SerializableArrayType(value.getClass().getComponentType());
        } else if (value instanceof Parcelable) {
            return new ParcelableType(value.getClass());
        } else if (value instanceof Enum) {
            return new EnumType(value.getClass());
        } else if (value instanceof Serializable) {
            return new SerializableType(value.getClass());
        } else {
            throw new IllegalArgumentException("Object of type " + value.getClass().getName()
                    + " is not supported for navigation arguments.");
        }
    }

    /**
     * NavType for storing integer values,
     * corresponding with the "integer" type in a Navigation XML file.
     * <p>
     * Null values are not supported.
     */
    @NonNull
    public static final NavType<Integer> IntType = new NavType<Integer>(false) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @NonNull Integer value) {
            bundle.putInt(key, value);
        }

        @Override
        public Integer get(@NonNull Bundle bundle, @NonNull String key) {
            return (Integer) bundle.get(key);
        }

        @NonNull
        @Override
        public Integer parseValue(@NonNull String value) {
            if (value.startsWith("0x")) {
                return Integer.parseInt(value.substring(2), 16);
            } else {
                return Integer.parseInt(value);
            }
        }

        @NonNull
        @Override
        public String getName() {
            return "integer";
        }
    };

    /**
     * NavType for storing integer values representing resource ids,
     * corresponding with the "reference" type in a Navigation XML file.
     * <p>
     * Null values are not supported.
     */
    @NonNull
    public static final NavType<Integer> ReferenceType = new NavType<Integer>(false) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key,
                @NonNull @AnyRes Integer value) {
            bundle.putInt(key, value);
        }

        @AnyRes
        @Override
        public Integer get(@NonNull Bundle bundle, @NonNull String key) {
            return (Integer) bundle.get(key);
        }

        @NonNull
        @Override
        public Integer parseValue(@NonNull String value) {
            throw new UnsupportedOperationException(
                    "References don't support parsing string values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "reference";
        }
    };

    /**
     * NavType for storing integer arrays,
     * corresponding with the "integer[]" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     */
    @NonNull
    public static final NavType<int[]> IntArrayType = new NavType<int[]>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable int[] value) {
            bundle.putIntArray(key, value);
        }

        @Override
        public int[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (int[]) bundle.get(key);
        }

        @NonNull
        @Override
        public int[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "integer[]";
        }
    };

    /**
     * NavType for storing long values,
     * corresponding with the "long" type in a Navigation XML file.
     * <p>
     * Null values are not supported.
     * Default values for this type in Navigation XML files must always end with an 'L' suffix, e.g.
     * `app:defaultValue="123L"`.
     */
    @NonNull
    public static final NavType<Long> LongType = new NavType<Long>(false) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @NonNull Long value) {
            bundle.putLong(key, value);
        }

        @Override
        public Long get(@NonNull Bundle bundle, @NonNull String key) {
            return (Long) bundle.get(key);
        }

        @NonNull
        @Override
        public Long parseValue(@NonNull String value) {
            //At runtime the L suffix is optional, contrary to the Safe Args plugin.
            //This is in order to be able to parse long numbers passed as deep link URL parameters
            if (value.endsWith("L")) {
                value = value.substring(0, value.length() - 1);
            }
            if (value.startsWith("0x")) {
                return Long.parseLong(value.substring(2), 16);
            } else {
                return Long.parseLong(value);
            }
        }

        @NonNull
        @Override
        public String getName() {
            return "long";
        }
    };

    /**
     * NavType for storing long arrays,
     * corresponding with the "long[]" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     */
    @NonNull
    public static final NavType<long[]> LongArrayType = new NavType<long[]>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable long[] value) {
            bundle.putLongArray(key, value);
        }

        @Override
        public long[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (long[]) bundle.get(key);
        }

        @NonNull
        @Override
        public long[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "long[]";
        }
    };

    /**
     * NavType for storing float values,
     * corresponding with the "float" type in a Navigation XML file.
     * <p>
     * Null values are not supported.
     */
    @NonNull
    public static final NavType<Float> FloatType = new NavType<Float>(false) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @NonNull Float value) {
            bundle.putFloat(key, value);
        }

        @Override
        public Float get(@NonNull Bundle bundle, @NonNull String key) {
            return (Float) bundle.get(key);
        }

        @NonNull
        @Override
        public Float parseValue(@NonNull String value) {
            return Float.parseFloat(value);
        }

        @NonNull
        @Override
        public String getName() {
            return "float";
        }
    };

    /**
     * NavType for storing float arrays,
     * corresponding with the "float[]" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     */
    @NonNull
    public static final NavType<float[]> FloatArrayType = new NavType<float[]>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable float[] value) {
            bundle.putFloatArray(key, value);
        }

        @Override
        public float[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (float[]) bundle.get(key);
        }

        @NonNull
        @Override
        public float[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "float[]";
        }
    };

    /**
     * NavType for storing boolean values,
     * corresponding with the "boolean" type in a Navigation XML file.
     * <p>
     * Null values are not supported.
     */
    @NonNull
    public static final NavType<Boolean> BoolType = new NavType<Boolean>(false) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @NonNull Boolean value) {
            bundle.putBoolean(key, value);
        }

        @Override
        public Boolean get(@NonNull Bundle bundle, @NonNull String key) {
            return (Boolean) bundle.get(key);
        }

        @NonNull
        @Override
        public Boolean parseValue(@NonNull String value) {
            if ("true".equals(value)) {
                return true;
            } else if ("false".equals(value)) {
                return false;
            } else {
                throw new IllegalArgumentException(
                        "A boolean NavType only accepts \"true\" or \"false\" values.");
            }
        }

        @NonNull
        @Override
        public String getName() {
            return "boolean";
        }
    };

    /**
     * NavType for storing boolean arrays,
     * corresponding with the "boolean[]" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     */
    @NonNull
    public static final NavType<boolean[]> BoolArrayType = new NavType<boolean[]>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable boolean[] value) {
            bundle.putBooleanArray(key, value);
        }

        @Override
        public boolean[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (boolean[]) bundle.get(key);
        }

        @NonNull
        @Override
        public boolean[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "boolean[]";
        }
    };

    /**
     * NavType for storing String values,
     * corresponding with the "string" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     */
    @NonNull
    public static final NavType<String> StringType = new NavType<String>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable String value) {
            bundle.putString(key, value);
        }

        @Override
        public String get(@NonNull Bundle bundle, @NonNull String key) {
            return (String) bundle.get(key);
        }

        @NonNull
        @Override
        public String parseValue(@NonNull String value) {
            return value;
        }

        @NonNull
        @Override
        public String getName() {
            return "string";
        }
    };

    /**
     * NavType for storing String arrays,
     * corresponding with the "string[]" type in a Navigation XML file.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     */
    @NonNull
    public static final NavType<String[]> StringArrayType = new NavType<String[]>(true) {
        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable String[] value) {
            bundle.putStringArray(key, value);
        }

        @Override
        public String[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (String[]) bundle.get(key);
        }

        @NonNull
        @Override
        public String[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @NonNull
        @Override
        public String getName() {
            return "string[]";
        }
    };

    /**
     * ParcelableType is used for passing Parcelables in {@link NavArgument}s.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     *
     * @param <D> the Parcelable class that is supported by this NavType
     */
    public static final class ParcelableType<D> extends NavType<D> {
        @NonNull
        private final Class<D> mType;

        /**
         * Constructs a NavType that supports a given Parcelable type.
         * @param type class that is a subtype of Parcelable
         */
        public ParcelableType(@NonNull Class<D> type) {
            super(true);
            if (!Parcelable.class.isAssignableFrom(type)
                    && !Serializable.class.isAssignableFrom(type)) {
                throw new IllegalArgumentException(
                        type + " does not implement Parcelable or Serializable.");
            }
            this.mType = type;
        }

        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable D value) {
            mType.cast(value);
            if (value == null || value instanceof Parcelable) {
                bundle.putParcelable(key, (Parcelable) value);
            } else if (value instanceof Serializable) {
                bundle.putSerializable(key, (Serializable) value);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        @Nullable
        public D get(@NonNull Bundle bundle, @NonNull String key) {
            return (D) bundle.get(key);
        }

        @NonNull
        @Override
        public D parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Parcelables don't support default values.");
        }

        @Override
        @NonNull
        public String getName() {
            return mType.getName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ParcelableType<?> that = (ParcelableType<?>) o;

            return mType.equals(that.mType);
        }

        @Override
        public int hashCode() {
            return mType.hashCode();
        }
    }

    /**
     * ParcelableArrayType is used for {@link NavArgument}s which hold arrays of Parcelables.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     *
     * @param <D> the type of Parcelable component class of the array
     */
    public static final class ParcelableArrayType<D extends Parcelable> extends NavType<D[]> {
        @NonNull
        private final Class<D[]> mArrayType;

        /**
         * Constructs a NavType that supports arrays of a given Parcelable type.
         * @param type class that is a subtype of Parcelable
         */
        @SuppressWarnings("unchecked")
        public ParcelableArrayType(@NonNull Class<D> type) {
            super(true);
            if (!Parcelable.class.isAssignableFrom(type)) {
                throw new IllegalArgumentException(
                        type + " does not implement Parcelable.");
            }

            Class<D[]> arrayType;
            try {
                arrayType = (Class<D[]>) Class.forName("[L" + type.getName() + ";");
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e); //should never happen
            }
            this.mArrayType = arrayType;
        }

        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable D[] value) {
            mArrayType.cast(value);
            bundle.putParcelableArray(key, value);
        }

        @SuppressWarnings("unchecked")
        @Override
        @Nullable
        public D[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (D[]) bundle.get(key);
        }

        @NonNull
        @Override
        public D[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @Override
        @NonNull
        public String getName() {
            return mArrayType.getName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ParcelableArrayType<?> that = (ParcelableArrayType<?>) o;

            return mArrayType.equals(that.mArrayType);
        }

        @Override
        public int hashCode() {
            return mArrayType.hashCode();
        }
    }

    /**
     * SerializableType is used for Serializable {@link NavArgument}s.
     * For handling Enums you must use {@link EnumType} instead.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     *
     * @param <D> the Serializable class that is supported by this NavType
     * @see EnumType
     */
    public static class SerializableType<D extends Serializable> extends NavType<D> {
        @NonNull
        private final Class<D> mType;

        /**
         * Constructs a NavType that supports a given Serializable type.
         * @param type class that is a subtype of Serializable
         */
        public SerializableType(@NonNull Class<D> type) {
            super(true);
            if (!Serializable.class.isAssignableFrom(type)) {
                throw new IllegalArgumentException(
                        type + " does not implement Serializable.");
            }
            if (type.isEnum()) {
                throw new IllegalArgumentException(
                        type + " is an Enum. You should use EnumType instead.");
            }
            this.mType = type;
        }

        SerializableType(boolean nullableAllowed, @NonNull Class<D> type) {
            super(nullableAllowed);
            if (!Serializable.class.isAssignableFrom(type)) {
                throw new IllegalArgumentException(
                        type + " does not implement Serializable.");
            }
            this.mType = type;
        }


        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable D value) {
            mType.cast(value);
            bundle.putSerializable(key, value);
        }

        @SuppressWarnings("unchecked")
        @Override
        @Nullable
        public D get(@NonNull Bundle bundle, @NonNull String key) {
            return (D) bundle.get(key);
        }

        @NonNull
        @Override
        public D parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Serializables don't support default values.");
        }

        @Override
        @NonNull
        public String getName() {
            return mType.getName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof SerializableType)) return false;

            SerializableType<?> that = (SerializableType<?>) o;

            return mType.equals(that.mType);
        }

        @Override
        public int hashCode() {
            return mType.hashCode();
        }
    }

    /**
     * EnumType is used for {@link NavArgument}s holding enum values.
     * <p>
     * Null values are not supported.
     * To specify a default value in a Navigation XML file, simply use the enum constant
     * without the class name, e.g. `app:defaultValue="MONDAY"`.
     *
     * @param <D> the Enum class that is supported by this NavType
     */
    public static final class EnumType<D extends Enum> extends SerializableType<D> {
        @NonNull
        private final Class<D> mType;

        /**
         * Constructs a NavType that supports a given Enum type.
         * @param type class that is an Enum
         */
        public EnumType(@NonNull Class<D> type) {
            super(false, type);
            if (!type.isEnum()) {
                throw new IllegalArgumentException(
                        type + " is not an Enum type.");
            }
            mType = type;
        }

        @SuppressWarnings("unchecked")
        @NonNull
        @Override
        public D parseValue(@NonNull String value) {
            for (Object constant : mType.getEnumConstants()) {
                if (((Enum) constant).name().equals(value)) {
                    return (D) constant;
                }
            }
            throw new IllegalArgumentException("Enum value " + value + " not found for type "
                    + mType.getName() + ".");
        }

        @Override
        @NonNull
        public String getName() {
            return mType.getName();
        }
    }

    /**
     * SerializableArrayType is used for {@link NavArgument}s that hold arrays of Serializables.
     * This type also supports arrays of Enums.
     * <p>
     * Null values are supported.
     * Default values in Navigation XML files are not supported.
     *
     * @param <D> the Serializable component class of the array
     */
    public static final class SerializableArrayType<D extends Serializable> extends NavType<D[]> {
        @NonNull
        private final Class<D[]> mArrayType;

        /**
         * Constructs a NavType that supports arrays of a given Serializable type.
         * @param type class that is a subtype of Serializable
         */
        @SuppressWarnings("unchecked")
        public SerializableArrayType(@NonNull Class<D> type) {
            super(true);
            if (!Serializable.class.isAssignableFrom(type)) {
                throw new IllegalArgumentException(
                        type + " does not implement Serializable.");
            }

            Class<D[]> arrayType;
            try {
                arrayType = (Class<D[]>) Class.forName("[L" + type.getName() + ";");
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e); //should never happen
            }
            this.mArrayType = arrayType;
        }

        @Override
        public void put(@NonNull Bundle bundle, @NonNull String key, @Nullable D[] value) {
            mArrayType.cast(value);
            bundle.putSerializable(key, value);
        }

        @SuppressWarnings("unchecked")
        @Override
        @Nullable
        public D[] get(@NonNull Bundle bundle, @NonNull String key) {
            return (D[]) bundle.get(key);
        }

        @NonNull
        @Override
        public D[] parseValue(@NonNull String value) {
            throw new UnsupportedOperationException("Arrays don't support default values.");
        }

        @Override
        @NonNull
        public String getName() {
            return mArrayType.getName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            SerializableArrayType<?> that = (SerializableArrayType<?>) o;

            return mArrayType.equals(that.mArrayType);
        }

        @Override
        public int hashCode() {
            return mArrayType.hashCode();
        }
    }
}