Return multiple method examples of different types in 11 programming languages

This article is shared from Huawei Cloud Community "Multi-language programming returns multiple different types of method examples", author: Zhang Jian.

background

You may encounter methods that need to return multiple different types in some scenarios. For example, when protocol parsing reads a message, or more specifically when kubernetes starts parsing Yaml, how does it know whether this type belongs to Deployment or Service?

C

C language usually implements this function by using Struct (structure) and Union (union), as shown in the following example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef enum {
    MONKEY,
    COW,
    UNKNOWN
} AnimalType;

typedef struct {
    char* description;
} Monkey;

typedef struct {
    char* description;
} Cow;

typedef struct {
    AnimalType type;
    union {
        Monkey monkey;
        Cow cow;
    };
} Animal;

Animal createAnimal(const char* animalType) {
    Animal animal;
    if (strcmp(animalType, "Monkey") == 0) {
        animal.type = MONKEY;
        animal.monkey.description = "I am a monkey!";
    } else if (strcmp(animalType, "Cow") == 0) {
        animal.type = COW;
        animal.cow.description = "I am a cow!";
    } else {
        animal.type = UNKNOWN;
    }
    return animal;
}

int main() {
    Animal animal1 = createAnimal("Monkey");
    if (animal1.type == MONKEY) {
        printf("%s\n", animal1.monkey.description);
    }

    Animal animal2 = createAnimal("Cow");
    if (animal2.type == COW) {
        printf("%s\n", animal2.cow.description);
    }

    Animal animal3 = createAnimal("Dog");
    if (animal3.type == UNKNOWN) {
        printf("Unknown animal type\n");
    }

    return 0;
}

C++

In C++, we can use base class pointers to point to objects of derived classes. You can use dynamic type identification (RTTI) to determine the type of an object at runtime

#include <iostream>
#include <stdexcept>

class Animal {
public:
    virtual std::string toString() const = 0;
};

class Monkey : public Animal {
public:
    std::string toString() const override {
        return "I am a monkey!";
    }
};

class Cow : public Animal {
public:
    std::string toString() const override {
        return "I am a cow!";
    }
};

Animal* createAnimal(const std::string& animalType) {
    if (animalType == "Monkey") {
        return new Monkey();
    }
    if (animalType == "Cow") {
        return new Cow();
    }
    throw std::runtime_error("Unknown animal type: " + animalType);
}

int main() {
    try {
        Animal* animal1 = createAnimal("Monkey");

        if (Monkey* monkey = dynamic_cast<Monkey*>(animal1)) {
            std::cout << monkey->toString() << std::endl;
        }
        delete animal1;

        Animal* animal2 = createAnimal("Cow");

        if (Cow* cow = dynamic_cast<Cow*>(animal2)) {
            std::cout << cow->toString() << std::endl;
        }
        delete animal2;
    }
    catch (const std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

Go

The common way of processing in Go is to return an interface or **interface{}** type. The caller uses Go language type assertions to check the specific type

package main

import (
	"fmt"
)

type Animal interface {
	String() string
}

type Monkey struct{}

func (m Monkey) String() string {
	return "I am a monkey!"
}

type Cow struct{}

func (c Cow) String() string {
	return "I am a cow!"
}

func createAnimal(typeName string) (Animal, error) {
	switch typeName {
	case "Monkey":
		return Monkey{}, nil
	case "Cow":
		return Cow{}, nil
	default:
		return nil, fmt.Errorf("Unknown animal type: %s", typeName)
	}
}

func main() {
	animal1, err := createAnimal("Monkey")
	if err != nil {
		fmt.Println(err)
		return
	}

	if monkey, ok := animal1.(Monkey); ok {
		fmt.Println(monkey)
	}

	animal2, err := createAnimal("Cow")
	if err != nil {
		fmt.Println(err)
		return
	}

	if cow, ok := animal2.(Cow); ok {
		fmt.Println(cow)
	}
}

Java

A common processing method in the Java language is to return the Object type or a basic type. Then the caller makes the instance of judgment. Or after Java17, you can use pattern matching to simplify the transformation

public class MultiTypeReturnExample {
    static class Monkey {
        @Override
        public String toString() {
            return "I am a monkey!";
        }
    }

    static class Cow {
        @Override
        public String toString() {
            return "I am a cow!";
        }
    }

    public static Object createAnimal(String type) throws IllegalArgumentException {
        switch (type) {
            case "Monkey":
                return new Monkey();
            case "Cow":
                return new Cow();
            default:
                throw new IllegalArgumentException("Unknown animal type: " + type);
        }
    }

    public static void main(String[] args) throws Exception {
        Object animal1 = createAnimal("Monkey");

        // java8 写法,后面如果明确用做精确的类型,需要强制转换

        if (animal1 instanceof Monkey) {
            System.out.println(animal1);
        }

        Object animal2 = createAnimal("Cow");
        if (animal2 instanceof Cow) {
            System.out.println(animal2);
        }

        // java17 写法,不需要强制转换
        if (createAnimal("Monkey") instanceof Monkey animal3) {
            System.out.println(animal3);
        }

        if (createAnimal("Cow") instanceof Cow animal4) {
            System.out.println(animal4);
        }
    }
}

Javascript

Dynamically typed language, use instanceof operator to determine

class Animal {
    toString() {
        return 'I am an animal';
    }
}

class Monkey extends Animal {
    toString() {
        return 'I am a monkey';
    }
}

class Cow extends Animal {
    toString() {
        return 'I am a cow';
    }
}

function createAnimal(animalType) {
    switch (animalType) {
        case 'Monkey':
            return new Monkey();
        case 'Cow':
            return new Cow();
        default:
            throw new Error(`Unknown animal type: ${animalType}`);
    }
}

try {
    const animal1 = createAnimal('Monkey');
    if (animal1 instanceof Monkey) {
        console.log(animal1.toString());
    }

    const animal2 = createAnimal('Cow');
    if (animal2 instanceof Cow) {
        console.log(animal2.toString());
    }

    const animal3 = createAnimal('Dog');
} catch (error) {
    console.error(error.message);
}

Kotlin

Kotlin can use Sealed Class (sealed class) and Any type in two ways. The scenario of using Any is similar to Java returning Object. Sealed Class is safer and more convenient.

Use Any type

open class Animal

class Monkey: Animal() {
    override fun toString(): String {
        return "I am a monkey!"
    }
}

class Cow: Animal() {
    override fun toString(): String {
        return "I am a cow!"
    }
}

fun createAnimal(type: String): Any {
    return when (type) {
        "Monkey" -> Monkey()
        "Cow" -> Cow()
        else -> throw IllegalArgumentException("Unknown animal type: $type")
    }
}

fun main() {
    val animal1 = createAnimal("Monkey")
    when (animal1) {
        is Monkey -> println(animal1)
        is Cow -> println(animal1)
    }

    val animal2 = createAnimal("Cow")
    when (animal2) {
        is Monkey -> println(animal2)
        is Cow -> println(animal2)
    }
}

UseSealedClass

sealed class Animal {
    data class Monkey(val info: String = "I am a monkey!") : Animal()
    data class Cow(val info: String = "I am a cow!") : Animal()
}

fun createAnimal(type: String): Animal {
    return when (type) {
        "Monkey" -> Animal.Monkey()
        "Cow" -> Animal.Cow()
        else -> throw IllegalArgumentException("Unknown animal type: $type")
    }
}

fun main() {
    val animal1 = createAnimal("Monkey")
    when (animal1) {
        is Animal.Monkey -> println(animal1.info)
        is Animal.Cow -> println(animal1.info)
    }

    val animal2 = createAnimal("Cow")
    when (animal2) {
        is Animal.Monkey -> println(animal2.info)
        is Animal.Cow -> println(animal2.info)
    }
}

Python

Python is a dynamically typed language that can simply return objects of different types based on some conditions, and then use the type() function or isinstance() function to determine its type after receiving the return value.

class Animal:
    def __str__(self):
        return "I am an animal"

class Monkey(Animal):
    def __str__(self):
        return "I am a monkey"

class Cow(Animal):
    def __str__(self):
        return "I am a cow"

def create_animal(animal_type):
    if animal_type == "Monkey":
        return Monkey()
    elif animal_type == "Cow":
        return Cow()
    else:
        raise ValueError(f"Unknown animal type: {animal_type}")

def main():
    animal1 = create_animal("Monkey")
    if isinstance(animal1, Monkey):
        print(animal1)

    animal2 = create_animal("Cow")
    if isinstance(animal2, Cow):
        print(animal2)

if __name__ == "__main__":
    main()

Ruby

Ruby is also relatively simple, returning objects of different types directly within the method. You can then use the is_a method or the class method to determine the Actual type.

class Animal
  def to_s
    "I am an animal"
  end
end

class Monkey < Animal
  def to_s
    "I am a monkey"
  end
end

class Cow < Animal
  def to_s
    "I am a cow"
  end
end

def create_animal(animal_type)
  case animal_type
  when "Monkey"
    Monkey.new
  when "Cow"
    Cow.new
  else
    raise "Unknown animal type: #{animal_type}"
  end
end

begin
  animal1 = create_animal("Monkey")
  if animal1.is_a? Monkey
    puts animal1
  end

  animal2 = create_animal("Cow")
  if animal2.is_a? Cow
    puts animal2
  end
end

Rust

In Rust, you can use enums to create a data structure that holds multiple different types. Then use the match statement to do pattern matching.

use std::fmt;

enum Animal {
    Monkey,
    Cow,
}

impl fmt::Display for Animal {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Animal::Monkey => write!(f, "I am a monkey!"),
            Animal::Cow => write!(f, "I am a cow!"),
        }
    }
}

fn create_animal(animal_type: &str) -> Result<Animal, String> {
    match animal_type {
        "Monkey" => Ok(Animal::Monkey),
        "Cow" => Ok(Animal::Cow),
        _ => Err(format!("Unknown animal type: {}", animal_type)),
    }
}

fn main() {
    match create_animal("Monkey") {
        Ok(animal) => match animal {
            Animal::Monkey => println!("{}", animal),
            _ => (),
        },
        Err(e) => println!("{}", e),
    }

    match create_animal("Cow") {
        Ok(animal) => match animal {
            Animal::Cow => println!("{}", animal),
            _ => (),
        },
        Err(e) => println!("{}", e),
    }

    match create_animal("Dog") {
        Ok(_) => (),
        Err(e) => println!("{}", e),
    }
}

Scala

In Scala, you can use the sealed trait and case class to create a method that can return multiple different types. The Sealed trait can define a limited set of subclasses to ensure type safety

sealed trait Animal {
  def info: String
}

case class Monkey() extends Animal {
  val info: String = "I am a monkey!"
}

case class Cow() extends Animal {
  val info: String = "I am a cow!"
}

object MultiTypeReturnExample {
  def createAnimal(animalType: String): Animal = {
    animalType match {
      case "Monkey" => Monkey()
      case "Cow" => Cow()
      case _ => throw new IllegalArgumentException(s"Unknown animal type: $animalType")
    }
  }

  def main(args: Array[String]): Unit = {
    try {
      val animal1 = createAnimal("Monkey")
      animal1 match {
        case Monkey() => println(animal1.info)
        case _ =>
      }

      val animal2 = createAnimal("Cow")
      animal2 match {
        case Cow() => println(animal2.info)
        case _ =>
      }
    } catch {
      case e: IllegalArgumentException => println(e.getMessage)
    }
  }
}

TypeScript

Generally speaking, it is not much different from JavaScript

abstract class Animal {
  abstract toString(): string;
}

class Monkey extends Animal {
  toString(): string {
    return 'I am a monkey';
  }
}

class Cow extends Animal {
  toString(): string {
    return 'I am a cow';
  }
}

function createAnimal(animalType: string): Animal {
  switch (animalType) {
    case 'Monkey':
      return new Monkey();
    case 'Cow':
      return new Cow();
    default:
      throw new Error(`Unknown animal type: ${animalType}`);
  }
}

try {
  const animal1 = createAnimal('Monkey');
  if (animal1 instanceof Monkey) {
    console.log(animal1.toString());
  }

  const animal2 = createAnimal('Cow');
  if (animal2 instanceof Cow) {
    console.log(animal2.toString());
  }

  const animal3 = createAnimal('Dog');
} catch (error) {
  console.error(error.message);
}

Click to follow and learn about Huawei Cloud’s new technologies as soon as possible~ 

おすすめ

転載: blog.csdn.net/devcloud/article/details/135000717