Java 返回抽象类的实例而不进行切片
有没有一种方法可以返回函数中抽象类的不同变体,而不会因切片而丢失信息 比如说,我有一个抽象的类狗,对于狗来说有野狗,家养狗,小狗 是否可以创建一个函数,该函数接受其中一个函数并返回它们,而无需切片信息 例如:Java 返回抽象类的实例而不进行切片,java,c++,abstract,Java,C++,Abstract,有没有一种方法可以返回函数中抽象类的不同变体,而不会因切片而丢失信息 比如说,我有一个抽象的类狗,对于狗来说有野狗,家养狗,小狗 是否可以创建一个函数,该函数接受其中一个函数并返回它们,而无需切片信息 例如: // Returns the type of dog you currently have Dog getCurrentDog() { return this.dog; } void setDog(Dog dog) { this.dog = dog; } 是否可以将Wi
// Returns the type of dog you currently have
Dog getCurrentDog() {
return this.dog;
}
void setDog(Dog dog) {
this.dog = dog;
}
是否可以将WildDog或PuppyDog传递给setDog并保留相应类中的所有信息。例如,PuppyDog有一个其他狗没有的drinkMilk()函数,但我仍然希望能够访问它
如果可能的话,Java的等价物是什么
我现在正在考虑的解决方案是为每个Dog实例设置一个getDog(),该实例返回特定的Dog实例,无论是Puppy还是WildDog。这样,我可以专门访问每个类中的代码。为了避免切片,您需要向
狗传递指针或引用。看
现在,您可以拥有以下功能:
// getCurrentDog is a PuppyDog
std::shared_ptr<Dog> myDog = someOwner.getCurrentDog();
myDog->drinkMilk();
正如@PaulMcKenzie指出的,这通常是一个设计缺陷,并偏离了面向对象编程(因为最终会出现“如果它是野狗
,那么就这样做;如果它是小狗
,那么就这样做”等)为了避免切片,你需要向狗
传递一个指针或引用。看
现在,您可以拥有以下功能:
// getCurrentDog is a PuppyDog
std::shared_ptr<Dog> myDog = someOwner.getCurrentDog();
myDog->drinkMilk();
正如@PaulMcKenzie所指出的,它通常是一个设计缺陷,并偏离了面向对象编程(因为最终会出现“如果它是一个野狗,那么就这样做;如果它是pupyDog
,那么就这样做”等)多态性通常更好地表示为包装类的实现细节。这为您提供了一个异构接口,例如,该接口可以存储在向量中,并且可以在不进行切片的情况下进行复制
我在下面提供了一个工作示例
请注意,各种类型的狗的实现实际上并不是多态的-dog
、puppy
和wild\u dog
多态性是作为通用dog类doggie
的实现细节引入的
#include <iostream>
#include <typeinfo>
#include <memory>
#include <string>
#include <utility>
#include <type_traits>
#include <vector>
// default cases
template<class T>
void do_howl(const T&)
{
std::cout << "a " << T::dog_type() << " cannot howl" << std::endl;
}
template<class T>
void do_be_cute(const T&)
{
std::cout << "a " << T::dog_type() << " is not cute" << std::endl;
}
// now provide specialisations
struct dog
{
dog(std::string s) : name(std::move(s)) {}
std::string name;
static const char* dog_type() { return "normal dog"; }
};
void do_howl(const dog& d)
{
std::cout << "the dog called " << d.name << " is howling" << std::endl;
}
struct puppy
{
puppy(std::string s) : name(std::move(s)) {}
std::string name;
std::string cute_noise() const { return "yip yip!"; }
static const char* dog_type() { return "puppy"; }
};
void do_be_cute(const puppy& p)
{
std::cout << "aww! the cute little puppy called " << p.name << " is barking: " << p.cute_noise() << std::endl;
}
struct wild_dog
{
static const char* dog_type() { return "wild dog"; }
};
void do_howl(const wild_dog& d)
{
std::cout << "the nameless wild dog called is howling" << std::endl;
}
struct doggy {
struct concept {
virtual void be_cute() const = 0;
virtual void howl() const = 0;
};
template<class T>
struct model : concept
{
model(T&& t) : _t(std::move(t)) {}
model(const T& t) : _t(t) {}
void be_cute() const override
{
do_be_cute(_t);
}
void howl() const override
{
do_howl(_t);
}
T _t;
};
void howl() const {
_impl->howl();
}
void be_cute() const {
_impl->be_cute();
}
template<class T, std::enable_if_t<!std::is_base_of<doggy, T>::value>* = nullptr>
doggy(T&& t) : _impl(std::make_shared<model<T>>(std::forward<T>(t)))
{}
std::shared_ptr<concept> _impl;
};
int main()
{
std::vector<doggy> dogs = {
doggy(dog("rover")),
doggy(puppy("poochums")),
doggy(wild_dog())
};
for (const auto& d : dogs)
{
d.howl();
d.be_cute();
}
return 0;
}
在这种特殊情况下,doggie
具有共享的实现语义。如果希望创建不同的副本,请将共享的\u ptr
替换为唯一的\u ptr
,提供副本构造函数,并向概念中添加克隆()
方法(在模型中有相应的实现) 多态性通常更好地表示为包装类的实现细节。这为您提供了一个异构接口,例如,该接口可以存储在向量中,并且可以在不进行切片的情况下进行复制
我在下面提供了一个工作示例
请注意,各种类型的狗的实现实际上并不是多态的-dog
、puppy
和wild\u dog
多态性是作为通用dog类doggie
的实现细节引入的
#include <iostream>
#include <typeinfo>
#include <memory>
#include <string>
#include <utility>
#include <type_traits>
#include <vector>
// default cases
template<class T>
void do_howl(const T&)
{
std::cout << "a " << T::dog_type() << " cannot howl" << std::endl;
}
template<class T>
void do_be_cute(const T&)
{
std::cout << "a " << T::dog_type() << " is not cute" << std::endl;
}
// now provide specialisations
struct dog
{
dog(std::string s) : name(std::move(s)) {}
std::string name;
static const char* dog_type() { return "normal dog"; }
};
void do_howl(const dog& d)
{
std::cout << "the dog called " << d.name << " is howling" << std::endl;
}
struct puppy
{
puppy(std::string s) : name(std::move(s)) {}
std::string name;
std::string cute_noise() const { return "yip yip!"; }
static const char* dog_type() { return "puppy"; }
};
void do_be_cute(const puppy& p)
{
std::cout << "aww! the cute little puppy called " << p.name << " is barking: " << p.cute_noise() << std::endl;
}
struct wild_dog
{
static const char* dog_type() { return "wild dog"; }
};
void do_howl(const wild_dog& d)
{
std::cout << "the nameless wild dog called is howling" << std::endl;
}
struct doggy {
struct concept {
virtual void be_cute() const = 0;
virtual void howl() const = 0;
};
template<class T>
struct model : concept
{
model(T&& t) : _t(std::move(t)) {}
model(const T& t) : _t(t) {}
void be_cute() const override
{
do_be_cute(_t);
}
void howl() const override
{
do_howl(_t);
}
T _t;
};
void howl() const {
_impl->howl();
}
void be_cute() const {
_impl->be_cute();
}
template<class T, std::enable_if_t<!std::is_base_of<doggy, T>::value>* = nullptr>
doggy(T&& t) : _impl(std::make_shared<model<T>>(std::forward<T>(t)))
{}
std::shared_ptr<concept> _impl;
};
int main()
{
std::vector<doggy> dogs = {
doggy(dog("rover")),
doggy(puppy("poochums")),
doggy(wild_dog())
};
for (const auto& d : dogs)
{
d.howl();
d.be_cute();
}
return 0;
}
在这种特殊情况下,doggie
具有共享的实现语义。如果希望创建不同的副本,请将共享的\u ptr
替换为唯一的\u ptr
,提供副本构造函数,并向概念中添加克隆()
方法(在模型中有相应的实现)在C++中,可以返回一个指针或引用到一个实例。如果不进行切片,则无法返回副本,因为编译器无法根据子类的超类来告诉子类的实例要保留多少内存。你是在用java或C++来讨论吗?在爪哇,没有任何切片,因为所有对象都是通过引用处理的。在C++中,如果不想切片,你应该通过引用:<代码>空隙设置狗(C++狗和狗)。克里托斯;这很奇怪,因为在我的Eclipse IDE中,它会告诉我“Dog”没有“drinkMilk”方法,因此不会编译。@Xari举例说,PuppyDog有一个其他Dog没有的drinkMilk()函数,但我仍然希望能够访问它。-然后,如果你调用的函数是“一般”< C++ >代码>函数,你就有一个设计缺陷。它是奇怪的,因为在我的Eclipse IDE中,它会告诉我“狗”没有方法“喝牛奶”,因此不会编译“-我不知道为什么你会感到惊讶,因为狗没有一个方法。您可以返回指向实例的指针或引用。如果不进行切片,则无法返回副本,因为编译器无法根据子类的超类来告诉子类的实例要保留多少内存。你是在用java或C++来讨论吗?在爪哇,没有任何切片,因为所有对象都是通过引用处理的。在C++中,如果不想切片,你应该通过引用:<代码>空隙设置狗(C++狗和狗)。克里托斯;这很奇怪,因为在我的Eclipse IDE中,它会告诉我“Dog”没有“drinkMilk”方法,因此不会编译。@Xari举例说,PuppyDog有一个其他Dog没有的drinkMilk()函数,但我仍然希望能够访问它。-如果您调用的函数是“general”Dog
函数,那么您就有了一个设计缺陷。@Xari“这很奇怪,因为在我的Eclipse IDE中,它会告诉我“Dog”没有“drinkMilk”方法,因此不会编译”-我不知道为什么这会让您感到惊讶,因为狗没有喝牛奶的方法。如果我只想让一只小狗喝牛奶呢?我知道在您的例子中,您在Dog类中定义了它,然后在PuppyDog中重写了它,但是如果我只想在PuppyDog中使用它呢。通过引用传递可以解决这个问题吗?您需要dynamic\u cast
。我将添加一个示例
#include <iostream>
#include <typeinfo>
#include <memory>
#include <string>
#include <utility>
#include <type_traits>
#include <vector>
// default cases
template<class T>
void do_howl(const T&)
{
std::cout << "a " << T::dog_type() << " cannot howl" << std::endl;
}
template<class T>
void do_be_cute(const T&)
{
std::cout << "a " << T::dog_type() << " is not cute" << std::endl;
}
// now provide specialisations
struct dog
{
dog(std::string s) : name(std::move(s)) {}
std::string name;
static const char* dog_type() { return "normal dog"; }
};
void do_howl(const dog& d)
{
std::cout << "the dog called " << d.name << " is howling" << std::endl;
}
struct puppy
{
puppy(std::string s) : name(std::move(s)) {}
std::string name;
std::string cute_noise() const { return "yip yip!"; }
static const char* dog_type() { return "puppy"; }
};
void do_be_cute(const puppy& p)
{
std::cout << "aww! the cute little puppy called " << p.name << " is barking: " << p.cute_noise() << std::endl;
}
struct wild_dog
{
static const char* dog_type() { return "wild dog"; }
};
void do_howl(const wild_dog& d)
{
std::cout << "the nameless wild dog called is howling" << std::endl;
}
struct doggy {
struct concept {
virtual void be_cute() const = 0;
virtual void howl() const = 0;
};
template<class T>
struct model : concept
{
model(T&& t) : _t(std::move(t)) {}
model(const T& t) : _t(t) {}
void be_cute() const override
{
do_be_cute(_t);
}
void howl() const override
{
do_howl(_t);
}
T _t;
};
void howl() const {
_impl->howl();
}
void be_cute() const {
_impl->be_cute();
}
template<class T, std::enable_if_t<!std::is_base_of<doggy, T>::value>* = nullptr>
doggy(T&& t) : _impl(std::make_shared<model<T>>(std::forward<T>(t)))
{}
std::shared_ptr<concept> _impl;
};
int main()
{
std::vector<doggy> dogs = {
doggy(dog("rover")),
doggy(puppy("poochums")),
doggy(wild_dog())
};
for (const auto& d : dogs)
{
d.howl();
d.be_cute();
}
return 0;
}
the dog called rover is howling
a normal dog is not cute
a puppy cannot howl
aww! the cute little puppy called poochums is barking: yip yip!
the nameless wild dog called is howling
a wild dog is not cute