OneOf

data class OneOf<out K : OneOf.Key<T>, T>(val key: K, val value: T)

A oneof's value and the key that identifies which field holds it.

Wire has two different mechanisms for generating oneof code: “boxed” that uses this class, and “flat” where all oneof fields are defined inline in the declaring message.

Flat oneofs

-----------

This is Wire's default mechanism. Each oneof field in the schema yields a field in the generated Kotlin or Java class. At most one field has a non-null value, and callers need to manually probe each field until they find the non-null value.

This is well-suited to oneofs with a small number of choices (less than 5).

Boxed oneofs

------------

In this mechanism the generated class has one field that holds an instance of this. It has both a key that identifies which field is populated, and its value. If no field is set, then the OneOf box is null.

This is well-suited to oneofs with a large number of choices (5 or more). This mechanism is necessary for oneofs with a very large number of options because in the other form the generated code may exceed the JVM's method size limits.

Opt-in to boxed oneofs in your build by setting the boxOneOfsMinSize option. OneOfs with this many fields or more will be generated in this form.

wire {
kotlin {
boxOneOfsMinSize = 10
}
}

Using Boxed oneofs

------------------

One challenge in using oneofs is coping with data from different schema versions that offer new unknown choices. For example, a client may receive a message from a server that sets a oneof field the client doesn't know about; or a server may read an archived message with a oneof field that has since been deleted.

In either case, the oneof will be null! There is no way to differentiate between unset and set to an unknown field. Please keep this in mind when writing code to handle oneofs.

In this example the address type may be absent (no value was ever set) or its type may be too new for the current code to understand. This code returns a default value:

val buttonLabel = when (contact.address?.key) {
Contact.addressSmsNumber -> "Send SMS"
Contact.addressEmailAddress -> "Send Email"
else -> "Address Type Unknown or Unset"
}

Another approach is to crash. In this case applications need to be careful to avoid receiving data with an incompatible schema.

val buttonLabel = when (contact.address?.key) {
Contact.addressSmsNumber -> "Send SMS"
Contact.addressEmailAddress -> "Send Email"
else -> throw IllegalStateException("unknown address!")
}

Consider AnyMessage

-------------------

New schemas should consider google.protobuf.Any instead of oneof.

Benefits of Any:

  • No build-time dependency from the referencing type on the referenced type.

  • Add new types without changing the schema

Benefits of OneOf:

  • More compact on-the-wire encoding. Approximately 1 byte of overhead for OneOf vs. 32 for the message name in an Any.

  • All choices are cataloged a central place in the schema.

Constructors

Link copied to clipboard
constructor(key: K, value: T)

Types

Link copied to clipboard
abstract class Key<T>(val tag: Int, val adapter: ProtoAdapter<T>, val declaredName: String, val redacted: Boolean = false, val jsonName: String = "")

Identifies a field in a OneOf. Typically subclasses are generated by the Wire compiler and instances are declared as members of the referencing message class.

Properties

Link copied to clipboard
val key: K
Link copied to clipboard
val value: T

Functions

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
fun <X> getOrNull(key: OneOf.Key<X>): X?
Link copied to clipboard
open override fun toString(): String